From 0249eefe53f2267a179a1271ae21b9c9af878ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Thu, 11 Jan 2024 22:54:30 +0100 Subject: Locale problems prior to OS X 10.4 no longer apply localeconv() returns the right values in later versions, and we only support 10.13+ --- indra/llui/llresmgr.cpp | 55 ------------------------------------------------- 1 file changed, 55 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp index d65c220974..23e0842f37 100644 --- a/indra/llui/llresmgr.cpp +++ b/indra/llui/llresmgr.cpp @@ -51,14 +51,6 @@ char LLResMgr::getDecimalPoint() const { char decimal = localeconv()->decimal_point[0]; -#if LL_DARWIN - // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped. - if(decimal == 0) - { - decimal = '.'; - } -#endif - return decimal; } @@ -66,14 +58,6 @@ char LLResMgr::getThousandsSeparator() const { char separator = localeconv()->thousands_sep[0]; -#if LL_DARWIN - // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped. - if(separator == 0) - { - separator = ','; - } -#endif - return separator; } @@ -81,14 +65,6 @@ char LLResMgr::getMonetaryDecimalPoint() const { char decimal = localeconv()->mon_decimal_point[0]; -#if LL_DARWIN - // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped. - if(decimal == 0) - { - decimal = '.'; - } -#endif - return decimal; } @@ -96,14 +72,6 @@ char LLResMgr::getMonetaryThousandsSeparator() const { char separator = localeconv()->mon_thousands_sep[0]; -#if LL_DARWIN - // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped. - if(separator == 0) - { - separator = ','; - } -#endif - return separator; } @@ -115,29 +83,6 @@ std::string LLResMgr::getMonetaryString( S32 input ) const LLLocale locale(LLLocale::USER_LOCALE); struct lconv *conv = localeconv(); - -#if LL_DARWIN - // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped. - // Fake up a conv structure with some reasonable values for the fields this function uses. - struct lconv fakeconv; - char fake_neg[2] = "-"; - char fake_mon_group[4] = "\x03\x03\x00"; // commas every 3 digits - if(conv->negative_sign[0] == 0) // Real locales all seem to have something here... - { - fakeconv = *conv; // start with what's there. - switch(mLocale) - { - default: // Unknown -- use the US defaults. - case LLLOCALE_USA: - case LLLOCALE_UK: // UK ends up being the same as US for the items used here. - fakeconv.negative_sign = fake_neg; - fakeconv.mon_grouping = fake_mon_group; - fakeconv.n_sign_posn = 1; // negative sign before the string - break; - } - conv = &fakeconv; - } -#endif char* negative_sign = conv->negative_sign; char separator = getMonetaryThousandsSeparator(); -- cgit v1.2.3 From 584ccc2f84e010cbf5cadad5da7651577b51946c Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 5 Feb 2024 12:56:00 +0100 Subject: #68175 PR-726 MacOS build fix --- indra/llui/lluictrl.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index be1c7dd0b6..f1b6746ea0 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -146,24 +146,24 @@ protected: // We shouldn't ever need to set this directly //virtual void setViewModel(const LLViewModelPtr&); - virtual BOOL postBuild(); + /*virtual*/ BOOL postBuild() override; public: // LLView interface - /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); - /*virtual*/ BOOL isCtrl() const; - /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL canFocusChildren() const; - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(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*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ) override; + /*virtual*/ BOOL isCtrl() const override; + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override; + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL canFocusChildren() const override; + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask) override; // From LLFocusableElement - /*virtual*/ void setFocus( BOOL b ); - /*virtual*/ BOOL hasFocus() const; + /*virtual*/ void setFocus( BOOL b ) override; + /*virtual*/ BOOL hasFocus() const override; // New virtuals @@ -318,7 +318,7 @@ protected: static F32 sActiveControlTransparency; static F32 sInactiveControlTransparency; - virtual void addInfo(LLSD & info); + /*virtual*/ void addInfo(LLSD & info) override; private: -- cgit v1.2.3 From 2b31dad40026d8078ea30d0da0656a4078d0f5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Fri, 9 Feb 2024 22:26:02 +0100 Subject: miscellaneous: BOOL (int) to real bool --- indra/llui/llfolderviewmodel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index 551a60e097..b3e3bc75e2 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -165,7 +165,7 @@ public: virtual BOOL isItemWearable() const { return FALSE; } virtual BOOL isItemRenameable() const = 0; - virtual BOOL renameItem(const std::string& new_name) = 0; + virtual bool renameItem(const std::string& new_name) = 0; virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder virtual void move( LLFolderViewModelItem* parent_listener ) = 0; -- cgit v1.2.3 From 4419bb870986c6900fc096338622d27b999cd771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 11 Feb 2024 01:23:28 +0100 Subject: more misc: BOOL (int) to real bool --- indra/llui/llflashtimer.cpp | 2 +- indra/llui/llflashtimer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llflashtimer.cpp b/indra/llui/llflashtimer.cpp index 39793316f4..d8418f1182 100644 --- a/indra/llui/llflashtimer.cpp +++ b/indra/llui/llflashtimer.cpp @@ -53,7 +53,7 @@ void LLFlashTimer::unset() mCallback = NULL; } -BOOL LLFlashTimer::tick() +bool LLFlashTimer::tick() { mIsCurrentlyHighlighted = !mIsCurrentlyHighlighted; diff --git a/indra/llui/llflashtimer.h b/indra/llui/llflashtimer.h index db8d49f009..50c51c0d2a 100644 --- a/indra/llui/llflashtimer.h +++ b/indra/llui/llflashtimer.h @@ -46,7 +46,7 @@ public: LLFlashTimer(callback_t cb = NULL, S32 count = 0, F32 period = 0.0); ~LLFlashTimer() {}; - /*virtual*/ BOOL tick(); + /*virtual*/ bool tick(); void startFlashing(); void stopFlashing(); -- cgit v1.2.3 From 9480a98cffaafa5826b8daad20020cf399bbbefc Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 16 Feb 2024 00:07:58 +0100 Subject: Replace most of BOOL with bool in llmath --- indra/llui/llfolderview.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 650ae9ae75..8033eaa00f 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -1476,10 +1476,10 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) } } - BOOL item_clicked = FALSE; - for (selected_items_t::iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + bool item_clicked{ false }; + for (const auto item : mSelectedItems) { - item_clicked |= (*item_it)->getRect().pointInRect(x, y); + item_clicked |= item->getRect().pointInRect(x, y); } if(!item_clicked && mSingleFolderMode) { -- cgit v1.2.3 From 088f2f4f6545ebc2ee01945938a40ae5c87ad27a Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 17 Feb 2024 00:51:13 +0100 Subject: More BOOL to bool replacements primarily in llappearance and llxml --- indra/llui/llxuiparser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index 138ba8bf02..c122ab1c9b 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -930,10 +930,10 @@ bool LLXUIParser::writeFlag(Parser& parser, const void* val_ptr, name_stack_t& s bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr) { - S32 value; + bool value; LLXUIParser& self = static_cast(parser); bool success = self.mCurReadNode->getBoolValue(1, &value); - *((bool*)val_ptr) = (value != FALSE); + *((bool*)val_ptr) = (value != false); return success; } -- cgit v1.2.3 From c285f59ce2a05703e3a1232fcaf3ee3aea714b3f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sun, 18 Feb 2024 12:52:19 +0100 Subject: Replace BOOL with bool in llwindow and dependent classes --- indra/llui/llaccordionctrl.cpp | 10 +-- indra/llui/llaccordionctrl.h | 4 +- indra/llui/llaccordionctrltab.cpp | 28 ++++---- indra/llui/llaccordionctrltab.h | 8 +-- indra/llui/llbutton.cpp | 36 +++++----- indra/llui/llbutton.h | 14 ++-- indra/llui/llcombobox.cpp | 10 +-- indra/llui/llcombobox.h | 4 +- indra/llui/llcontainerview.cpp | 12 ++-- indra/llui/llcontainerview.h | 6 +- indra/llui/lldraghandle.cpp | 20 +++--- indra/llui/lldraghandle.h | 6 +- indra/llui/llfloater.cpp | 34 ++++----- indra/llui/llfloater.h | 12 ++-- indra/llui/llfolderview.cpp | 32 ++++----- indra/llui/llfolderview.h | 10 +-- indra/llui/llfolderviewitem.cpp | 70 +++++++++--------- indra/llui/llfolderviewitem.h | 18 ++--- indra/llui/lliconctrl.cpp | 4 +- indra/llui/lliconctrl.h | 2 +- indra/llui/lllineeditor.cpp | 70 +++++++++--------- indra/llui/lllineeditor.h | 16 ++--- indra/llui/llmenubutton.cpp | 4 +- indra/llui/llmenubutton.h | 2 +- indra/llui/llmenugl.cpp | 148 +++++++++++++++++++------------------- indra/llui/llmenugl.h | 44 ++++++------ indra/llui/llmodaldialog.cpp | 24 +++---- indra/llui/llmodaldialog.h | 12 ++-- indra/llui/llmultislider.cpp | 18 ++--- indra/llui/llmultislider.h | 6 +- indra/llui/llradiogroup.cpp | 6 +- indra/llui/llresizebar.cpp | 26 +++---- indra/llui/llresizebar.h | 8 +-- indra/llui/llresizehandle.cpp | 22 +++--- indra/llui/llresizehandle.h | 6 +- indra/llui/llscrollbar.cpp | 34 ++++----- indra/llui/llscrollbar.h | 12 ++-- indra/llui/llscrollcontainer.cpp | 24 +++---- indra/llui/llscrollcontainer.h | 6 +- indra/llui/llscrolllistcolumn.cpp | 4 +- indra/llui/llscrolllistcolumn.h | 2 +- indra/llui/llscrolllistctrl.cpp | 52 +++++++------- indra/llui/llscrolllistctrl.h | 18 ++--- indra/llui/llslider.cpp | 22 +++--- indra/llui/llslider.h | 8 +-- indra/llui/llspinctrl.cpp | 4 +- indra/llui/llspinctrl.h | 2 +- indra/llui/llstatbar.cpp | 22 +++--- indra/llui/llstatbar.h | 4 +- indra/llui/lltabcontainer.cpp | 28 ++++---- indra/llui/lltabcontainer.h | 8 +-- indra/llui/lltextbase.cpp | 110 ++++++++++++++-------------- indra/llui/lltextbase.h | 58 +++++++-------- indra/llui/lltextbox.cpp | 18 ++--- indra/llui/lltextbox.h | 6 +- indra/llui/lltexteditor.cpp | 66 ++++++++--------- indra/llui/lltexteditor.h | 18 ++--- indra/llui/lltoolbar.cpp | 12 ++-- indra/llui/lltoolbar.h | 6 +- indra/llui/lltooltip.cpp | 20 +++--- indra/llui/lltooltip.h | 12 ++-- indra/llui/lluictrl.cpp | 20 +++--- indra/llui/lluictrl.h | 10 +-- indra/llui/llview.cpp | 34 ++++----- indra/llui/llview.h | 26 +++---- indra/llui/llvirtualtrackball.cpp | 12 ++-- indra/llui/llvirtualtrackball.h | 8 +-- indra/llui/llxyvector.cpp | 12 ++-- indra/llui/llxyvector.h | 6 +- 69 files changed, 728 insertions(+), 728 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 0a82bed896..a5417d054e 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -199,7 +199,7 @@ void LLAccordionCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) } //--------------------------------------------------------------------------------- -BOOL LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) { return LLPanel::handleRightMouseDown(x, y, mask); } @@ -552,13 +552,13 @@ void LLAccordionCtrl::arrange() //--------------------------------------------------------------------------------- -BOOL LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) { if (LLPanel::handleScrollWheel(x, y, clicks)) - return TRUE; + return true; if (mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) - return TRUE; - return FALSE; + return true; + return false; } BOOL LLAccordionCtrl::handleKeyHere(KEY key, MASK mask) diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h index 6a1989afba..ba5a45759f 100644 --- a/indra/llui/llaccordionctrl.h +++ b/indra/llui/llaccordionctrl.h @@ -90,8 +90,8 @@ public: virtual BOOL postBuild(); - virtual BOOL handleRightMouseDown ( S32 x, S32 y, MASK mask); - virtual BOOL handleScrollWheel ( S32 x, S32 y, S32 clicks ); + virtual bool handleRightMouseDown ( S32 x, S32 y, MASK mask); + virtual bool handleScrollWheel ( S32 x, S32 y, S32 clicks ); virtual BOOL handleKeyHere (KEY key, MASK mask); virtual BOOL handleDragAndDrop (S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 20da568746..c3787acec2 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -482,7 +482,7 @@ void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl) LLUICtrl::onUpdateScrollToChild(cntrl); } -BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) { if (mCollapsible && mHeaderVisible && mCanOpenClose) { @@ -493,13 +493,13 @@ BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) // Reset stored state mWasStateStored = false; - return TRUE; + return true; } } return LLUICtrl::handleMouseDown(x,y,mask); } -BOOL LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask) { return LLUICtrl::handleMouseUp(x,y,mask); } @@ -820,7 +820,7 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) if ((key == KEY_RETURN) && mask == MASK_NONE) { changeOpenClose(getDisplayChildren()); - return TRUE; + return true; } if ((key == KEY_ADD || key == KEY_RIGHT) && mask == MASK_NONE) @@ -828,7 +828,7 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) if (!getDisplayChildren()) { changeOpenClose(getDisplayChildren()); - return TRUE; + return true; } } @@ -837,7 +837,7 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) if (getDisplayChildren()) { changeOpenClose(getDisplayChildren()); - return TRUE; + return true; } } @@ -853,7 +853,7 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) { getAccordionView()->notify(LLSD().with("action", "select_first")); } - return TRUE; + return true; } if (key == KEY_UP && mask == MASK_NONE) @@ -862,7 +862,7 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) // we're processing notifyParent so let call parent directly getParent()->notifyParent(LLSD().with("action", "select_prev")); - return TRUE; + return true; } return LLUICtrl::handleKey(key, mask, called_from_parent); @@ -1097,7 +1097,7 @@ void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, panel->setRect(panel_rect); } -BOOL LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) +bool LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) { //header may be not the first child but we need to process it first if (y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT / 2)) @@ -1105,22 +1105,22 @@ BOOL LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) //inside tab header //fix for EXT-6619 mHeader->handleToolTip(x, y, mask); - return TRUE; + return true; } return LLUICtrl::handleToolTip(x, y, mask); } -BOOL LLAccordionCtrlTab::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLAccordionCtrlTab::handleScrollWheel(S32 x, S32 y, S32 clicks) { if (LLUICtrl::handleScrollWheel(x, y, clicks)) { - return TRUE; + return true; } if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) { - return TRUE; + return true; } - return FALSE; + return false; } diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index 896a34cac4..161f5c6361 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -162,13 +162,13 @@ public: virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); // Changes expand/collapse state and triggers expand/collapse callbacks - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); - virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); - virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + virtual bool handleToolTip(S32 x, S32 y, MASK mask); + virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); virtual bool addChild(LLView* child, S32 tab_group = 0 ); diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 49d275997a..d8589444fb 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -393,9 +393,9 @@ BOOL LLButton::postBuild() return LLUICtrl::postBuild(); } -BOOL LLButton::handleUnicodeCharHere(llwchar uni_char) +bool LLButton::handleUnicodeCharHere(llwchar uni_char) { - BOOL handled = FALSE; + bool handled = false; if(' ' == uni_char && !gKeyboard->getKeyRepeated(' ')) { @@ -406,9 +406,9 @@ BOOL LLButton::handleUnicodeCharHere(llwchar uni_char) LLUICtrl::onCommit(); - handled = TRUE; + handled = true; } - return handled; + return handled; } BOOL LLButton::handleKeyHere(KEY key, MASK mask ) @@ -429,7 +429,7 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask ) } -BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLButton::handleMouseDown(S32 x, S32 y, MASK mask) { if (!childrenHandleMouseDown(x, y, mask)) { @@ -438,7 +438,7 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask) if (hasTabStop() && !getIsChrome()) { - setFocus(TRUE); + setFocus(true); } if (!mFunctionName.empty()) @@ -469,11 +469,11 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask) make_ui_sound("UISndClick"); } } - return TRUE; + return true; } -BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLButton::handleMouseUp(S32 x, S32 y, MASK mask) { // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) @@ -518,10 +518,10 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) childrenHandleMouseUp(x, y, mask); } - return TRUE; + return true; } -BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) { if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask)) { @@ -530,7 +530,7 @@ BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) if (hasTabStop() && !getIsChrome()) { - setFocus(TRUE); + setFocus(true); } // if (pointInView(x, y)) @@ -543,10 +543,10 @@ BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) // if they are not mouse opaque. } - return TRUE; + return true; } -BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask) +bool LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask) { if (mHandleRightMouse) { @@ -572,7 +572,7 @@ BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask) // but this might change the mouse handling of existing buttons in a bad way. // if they are not mouse opaque. } - return TRUE; + return true; } void LLButton::onMouseLeave(S32 x, S32 y, MASK mask) @@ -587,11 +587,11 @@ void LLButton::setHighlight(bool b) mNeedsHighlight = b; } -BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) +bool LLButton::handleHover(S32 x, S32 y, MASK mask) { if (isInEnabledChain() && (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)) - mNeedsHighlight = TRUE; + mNeedsHighlight = true; if (!childrenHandleHover(x, y, mask)) { @@ -610,7 +610,7 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) getWindow()->setCursor(UI_CURSOR_ARROW); LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL; } - return TRUE; + return true; } void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height) @@ -1308,7 +1308,7 @@ void LLButton::resetMouseDownTimer() mMouseDownTimer.reset(); } -BOOL LLButton::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLButton::handleDoubleClick(S32 x, S32 y, MASK mask) { // just treat a double click as a second click return handleMouseDown(x, y, mask); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index ccd31e90c0..0c99f6a343 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -155,14 +155,14 @@ public: void addImageAttributeToXML(LLXMLNodePtr node, const std::string& imageName, const LLUUID& imageID,const std::string& xmlTagName) const; - virtual BOOL handleUnicodeCharHere(llwchar uni_char); + 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 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*/ BOOL postBuild(); diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 9ca05a16f3..81031508ec 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -782,13 +782,13 @@ void LLComboBox::onItemSelected(const LLSD& data) onCommit(); } -BOOL LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) +bool LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) { std::string tool_tip; if(LLUICtrl::handleToolTip(x, y, mask)) { - return TRUE; + return true; } tool_tip = getToolTip(); @@ -803,7 +803,7 @@ BOOL LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) .message(tool_tip) .sticky_rect(calcScreenRect())); } - return TRUE; + return true; } BOOL LLComboBox::handleKeyHere(KEY key, MASK mask) @@ -852,9 +852,9 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask) return result; } -BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char) +bool LLComboBox::handleUnicodeCharHere(llwchar uni_char) { - BOOL result = FALSE; + bool result = false; if (gFocusMgr.childHasKeyboardFocus(this)) { // space bar just shows the list diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index cac8850a25..d87ce9189e 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -111,9 +111,9 @@ public: // LLView interface virtual void onFocusLost(); - virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); + virtual bool handleToolTip(S32 x, S32 y, MASK mask); virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL handleUnicodeCharHere(llwchar uni_char); + virtual bool handleUnicodeCharHere(llwchar uni_char); // LLUICtrl interface virtual void clear(); // select nothing diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp index 727fbe850e..415cdced4d 100644 --- a/indra/llui/llcontainerview.cpp +++ b/indra/llui/llcontainerview.cpp @@ -74,14 +74,14 @@ bool LLContainerView::addChild(LLView* child, S32 tab_group) return res; } -BOOL LLContainerView::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLContainerView::handleDoubleClick(S32 x, S32 y, MASK mask) { return handleMouseDown(x, y, mask); } -BOOL LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if (mDisplayChildren) { handled = (LLView::childrenHandleMouseDown(x, y, mask) != NULL); @@ -92,15 +92,15 @@ BOOL LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask) { setDisplayChildren(!mDisplayChildren); reshape(getRect().getWidth(), getRect().getHeight(), FALSE); - handled = TRUE; + handled = true; } } return handled; } -BOOL LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if (mDisplayChildren) { handled = (LLView::childrenHandleMouseUp(x, y, mask) != NULL); diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h index 99267d978a..f439689ceb 100644 --- a/indra/llui/llcontainerview.h +++ b/indra/llui/llcontainerview.h @@ -68,9 +68,9 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ void draw(); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index 220f5ee825..b2e342411c 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -271,7 +271,7 @@ void LLDragHandleLeft::reshape(S32 width, S32 height, BOOL called_from_parent) // UI event handling //------------------------------------------------------------- -BOOL LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) { // Route future Mouse messages here preemptively. (Release on mouse up.) // No handler needed for focus lost since this clas has no state that depends on it. @@ -282,11 +282,11 @@ BOOL LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) mLastMouseScreenY = mDragLastScreenY; // Note: don't pass on to children - return TRUE; + return true; } -BOOL LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask) { if( hasMouseCapture() ) { @@ -295,13 +295,13 @@ BOOL LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask) } // Note: don't pass on to children - return TRUE; + return true; } -BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask) +bool LLDragHandle::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) @@ -324,11 +324,11 @@ BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask) delta_y >= SLOP) { parent->setDocked(false, false); - return TRUE; + return true; } else { - return FALSE; + return false; } } @@ -367,13 +367,13 @@ BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask) getWindow()->setCursor(UI_CURSOR_ARROW); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" <setCursor(UI_CURSOR_ARROW); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = TRUE; + handled = true; } // Note: don't pass on to children diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h index e095e577b1..7f6ae47201 100644 --- a/indra/llui/lldraghandle.h +++ b/indra/llui/lldraghandle.h @@ -72,9 +72,9 @@ public: virtual void setTitle( const std::string& title ) = 0; virtual std::string getTitle() const = 0; - virtual BOOL handleHover(S32 x, S32 y, 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 handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); protected: LLDragHandle(const Params&); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 2303cd24b7..e90dab1a99 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1561,17 +1561,17 @@ BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index return FALSE; } -BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks) { LLPanel::handleScrollWheel(x,y,clicks); - return TRUE;//always + return true;//always } // virtual -BOOL LLFloater::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLFloater::handleMouseUp(S32 x, S32 y, MASK mask) { LL_DEBUGS() << "LLFloater::handleMouseUp calling LLPanel (really LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; - BOOL handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView + bool handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView if (handled) { LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); } @@ -1579,7 +1579,7 @@ BOOL LLFloater::handleMouseUp(S32 x, S32 y, MASK mask) } // virtual -BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLFloater::handleMouseDown(S32 x, S32 y, MASK mask) { if( mMinimized ) { @@ -1587,19 +1587,19 @@ BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask) // Note: this block and the offerClickToButton helper method can be removed // because the parent container will handle it for us but we'll keep it here // for safety until after reworking the panel code to manage hidden children. - if(offerClickToButton(x, y, mask, BUTTON_CLOSE)) return TRUE; - if(offerClickToButton(x, y, mask, BUTTON_RESTORE)) return TRUE; - if(offerClickToButton(x, y, mask, BUTTON_TEAR_OFF)) return TRUE; - if(offerClickToButton(x, y, mask, BUTTON_DOCK)) return TRUE; + if(offerClickToButton(x, y, mask, BUTTON_CLOSE)) return true; + if(offerClickToButton(x, y, mask, BUTTON_RESTORE)) return true; + if(offerClickToButton(x, y, mask, BUTTON_TEAR_OFF)) return true; + if(offerClickToButton(x, y, mask, BUTTON_DOCK)) return true; - setFrontmost(TRUE, FALSE); + setFrontmost(true, false); // Otherwise pass to drag handle for movement return mDragHandle->handleMouseDown(x, y, mask); } else { bringToFront( x, y ); - BOOL handled = LLPanel::handleMouseDown( x, y, mask ); + bool handled = LLPanel::handleMouseDown( x, y, mask ); if (handled) { LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); } @@ -1608,14 +1608,14 @@ BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask) } // virtual -BOOL LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL was_minimized = mMinimized; + bool was_minimized = mMinimized; bringToFront( x, y ); return was_minimized || LLPanel::handleRightMouseDown( x, y, mask ); } -BOOL LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +bool LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { bringToFront( x, y ); return LLPanel::handleMiddleMouseDown( x, y, mask ); @@ -1623,10 +1623,10 @@ BOOL LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask) // virtual -BOOL LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL was_minimized = mMinimized; - setMinimized(FALSE); + bool was_minimized = mMinimized; + setMinimized(false); return was_minimized || LLPanel::handleDoubleClick(x, y, mask); } diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 3d15708295..99ec77fa4d 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -293,13 +293,13 @@ public: S32 getMinHeight() const{ return mMinHeight; } S32 getHeaderHeight() const { return mHeaderHeight; } - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); + virtual bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 mask); + virtual bool handleScrollWheel(S32 x, S32 y, S32 mask); virtual void draw(); virtual void drawShadow(LLPanel* panel); diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 8033eaa00f..c9f74c63f8 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -1310,20 +1310,20 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) } -BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) +bool LLFolderView::handleUnicodeCharHere(llwchar uni_char) { if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL { - return FALSE; + return false; } if (uni_char > 0x7f) { LL_WARNS() << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << LL_ENDL; - return FALSE; + return false; } - BOOL handled = FALSE; + bool handled = false; if (mParentPanel.get()->hasFocus()) { // SL-51858: Key presses are not being passed to the Popup menu. @@ -1344,21 +1344,21 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) { mSearchString += uni_char; } - search(getCurSelectedItem(), mSearchString, FALSE); + search(getCurSelectedItem(), mSearchString, false); - handled = TRUE; + handled = true; } return handled; } -BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) +bool LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) { - mKeyboardSelection = FALSE; + mKeyboardSelection = false; mSearchString.clear(); - mParentPanel.get()->setFocus(TRUE); + mParentPanel.get()->setFocus(true); LLEditMenuHandler::gEditMenuHandler = this; @@ -1432,19 +1432,19 @@ BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &searc return found; } -BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) +bool LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) { // skip LLFolderViewFolder::handleDoubleClick() return LLView::handleDoubleClick( x, y, mask ); } -BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) +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 - mParentPanel.get()->setFocus(TRUE); + mParentPanel.get()->setFocus(true); - BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; + bool handled = childrenHandleRightMouseDown(x, y, mask) != NULL; S32 count = mSelectedItems.size(); LLMenuGL* menu = static_cast(mPopupMenuHandle.get()); @@ -1516,9 +1516,9 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) { if (menu && menu->getVisible()) { - menu->setVisible(FALSE); + menu->setVisible(false); } - setSelection(NULL, FALSE, TRUE); + setSelection(NULL, false, true); } return handled; } @@ -1554,7 +1554,7 @@ BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const return FALSE; } -BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) +bool LLFolderView::handleHover( S32 x, S32 y, MASK mask ) { return LLView::handleHover( x, y, mask ); } diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index 5f8a173889..b235de95e9 100644 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -202,11 +202,11 @@ public: // LLView functionality ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); - /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char); - /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); + /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleDoubleClick( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleRightMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask ); /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 0dc66bf37a..9069a95c9f 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -550,21 +550,21 @@ const std::string& LLFolderViewItem::getName( void ) const } // LLView functionality -BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) +bool LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) { if(!mIsSelected) { - getRoot()->setSelection(this, FALSE); + getRoot()->setSelection(this, false); } make_ui_sound("UISndClick"); - return TRUE; + return true; } -BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) +bool LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) { if (LLView::childrenHandleMouseDown(x, y, mask)) { - return TRUE; + return true; } // No handler needed for focus lost since this class has no @@ -583,7 +583,7 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) } else { - getRoot()->setSelection(this, FALSE); + getRoot()->setSelection(this, false); } make_ui_sound("UISndClick"); } @@ -591,15 +591,15 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) { // If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. // This is necessary so we maintain selection consistent when starting a drag. - mSelectPending = TRUE; + mSelectPending = true; } mDragStartX = x; mDragStartY = y; - return TRUE; + return true; } -BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) +bool LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) { static LLCachedControl drag_and_drop_threshold(*LLUI::getInstance()->mSettingGroups["config"],"DragAndDropDistanceThreshold", 3); @@ -616,7 +616,7 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) { // RN: when starting drag and drop, clear out last auto-open root->autoOpenTest(NULL); - root->setShowSelectionContext(TRUE); + root->setShowSelectionContext(true); // Release keyboard focus, so that if stuff is dropped into the // world, pressing the delete key won't blow away the inventory @@ -631,31 +631,31 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) } root->clearHoveredItem(); - return TRUE; + return true; } else { LLFolderView* pRoot = getRoot(); pRoot->setHoveredItem(this); - pRoot->setShowSelectionContext(FALSE); + pRoot->setShowSelectionContext(false); getWindow()->setCursor(UI_CURSOR_ARROW); // let parent handle this then... - return FALSE; + return false; } } -BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) +bool LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) { openItem(); - return TRUE; + return true; } -BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) +bool LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) { if (LLView::childrenHandleMouseUp(x, y, mask)) { - return TRUE; + return true; } // if mouse hasn't moved since mouse down... @@ -672,21 +672,21 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) } else { - getRoot()->setSelection(this, FALSE); + getRoot()->setSelection(this, false); } } - mSelectPending = FALSE; + mSelectPending = false; if( hasMouseCapture() ) { if (getRoot()) { - getRoot()->setShowSelectionContext(FALSE); + getRoot()->setShowSelectionContext(false); } gFocusMgr.setMouseCapture( NULL ); } - return TRUE; + return true; } void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask) @@ -2019,9 +2019,9 @@ BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, } -BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) +bool LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; if( isOpen() ) { @@ -2035,11 +2035,11 @@ BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) } -BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) +bool LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) { mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); - BOOL handled = LLView::handleHover(x, y, mask); + bool handled = LLView::handleHover(x, y, mask); if (!handled) { @@ -2050,9 +2050,9 @@ BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) return handled; } -BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) +bool LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; if( isOpen() ) { handled = childrenHandleMouseDown(x,y,mask) != NULL; @@ -2063,7 +2063,7 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) && !mSingleFolderMode) { toggleOpen(); - handled = TRUE; + handled = true; } else { @@ -2075,9 +2075,9 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) return handled; } -BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) +bool LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; if(mSingleFolderMode) { static LLUICachedControl double_click_new_window("SingleModeDoubleClickOpenWindow", false); @@ -2094,7 +2094,7 @@ BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) getViewModelItem()->navigateToFolder(false); }); } - return TRUE; + return true; } if( isOpen() ) @@ -2109,12 +2109,12 @@ BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) if (double_click_action == 1) { getViewModelItem()->navigateToFolder(true); - return TRUE; + return true; } if (double_click_action == 2) { getViewModelItem()->navigateToFolder(false, true); - return TRUE; + return true; } } if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) @@ -2125,10 +2125,10 @@ BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) } else { - getRoot()->setSelection(this, FALSE); + getRoot()->setSelection(this, false); toggleOpen(); } - handled = TRUE; + handled = true; } return handled; } diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 5c2a1ecff0..d178aa40d5 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -284,11 +284,11 @@ public: bool isSingleFolderMode() { return mSingleFolderMode; } // LLView functionality - virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleHover( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); - virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); + virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask ); + virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); + virtual bool handleHover( S32 x, S32 y, MASK mask ); + virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); + virtual bool handleDoubleClick( S32 x, S32 y, MASK mask ); virtual void onMouseLeave(S32 x, S32 y, MASK mask); @@ -447,10 +447,10 @@ public: virtual void openItem( void ); // LLView functionality - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleDoubleClick( 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 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, diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 2791377a5e..c774a1105f 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -86,12 +86,12 @@ void LLIconCtrl::draw() LLUICtrl::draw(); } -BOOL LLIconCtrl::handleHover(S32 x, S32 y, MASK mask) +bool LLIconCtrl::handleHover(S32 x, S32 y, MASK mask) { if (mInteractable && getEnabled()) { getWindow()->setCursor(UI_CURSOR_HAND); - return TRUE; + return true; } return LLUICtrl::handleHover(x, y, mask); } diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index e983d63a01..76b24cae52 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -71,7 +71,7 @@ public: virtual void draw(); // llview overrides - virtual BOOL handleHover(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); // lluictrl overrides void onVisibilityChange(BOOL new_visibility); diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 60dbfd68c6..a1b9b5696c 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -660,9 +660,9 @@ void LLLineEditor::onSpellCheckSettingsChange() mSpellCheckStart = mSpellCheckEnd = -1; } -BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { - setFocus( TRUE ); + setFocus( true ); mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); if (mSelectionEnd == 0 && mSelectionStart == mText.length()) @@ -674,7 +674,7 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { const LLWString& wtext = mText.getWString(); - BOOL doSelectAll = TRUE; + bool doSelectAll = true; // Select the word we're on if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) @@ -709,7 +709,7 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // We don't want handleMouseUp() to "finish" the selection (and thereby // set mSelectionEnd to where the mouse is), so we finish the selection // here. - mIsSelecting = FALSE; + mIsSelecting = false; // delay cursor flashing mKeystrokeTimer.reset(); @@ -717,15 +717,15 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // take selection to 'primary' clipboard updatePrimary(); - return TRUE; + return true; } -BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) { // Check first whether the "clear search" button wants to deal with this. if(childrenHandleMouseDown(x, y, mask) != NULL) { - return TRUE; + return true; } if (!mSelectAllonFocusReceived @@ -737,7 +737,7 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) if (mask & MASK_SHIFT) { // assume we're starting a drag select - mIsSelecting = TRUE; + mIsSelecting = true; // Handle selection extension S32 old_cursor_pos = getCursor(); @@ -793,14 +793,14 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) // We don't want handleMouseUp() to "finish" the selection (and thereby // set mSelectionEnd to where the mouse is), so we finish the selection // here. - mIsSelecting = FALSE; + mIsSelecting = false; } } gFocusMgr.setMouseCapture( this ); } - setFocus(TRUE); + setFocus(true); // delay cursor flashing mKeystrokeTimer.reset(); @@ -808,40 +808,40 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) if (mMouseDownSignal) (*mMouseDownSignal)(this,x,y,mask); - return TRUE; + return true; } -BOOL LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { // LL_INFOS() << "MiddleMouseDown" << LL_ENDL; - setFocus( TRUE ); + setFocus( true ); if( canPastePrimary() ) { setCursorAtLocalPos(x); pastePrimary(); } - return TRUE; + return true; } -BOOL LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) { - setFocus(TRUE); + setFocus(true); if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu()) { showContextMenu(x, y); } - return TRUE; + return true; } -BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // Check first whether the "clear search" button wants to deal with this. if(!hasMouseCapture()) { if(childrenHandleHover(x, y, mask) != NULL) { - return TRUE; + return true; } } @@ -884,34 +884,34 @@ BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask) getWindow()->setCursor(UI_CURSOR_IBEAM); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - handled = TRUE; + handled = true; } if( !handled ) { getWindow()->setCursor(UI_CURSOR_IBEAM); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = TRUE; + handled = true; } return handled; } -BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( hasMouseCapture() ) { gFocusMgr.setMouseCapture( NULL ); - handled = TRUE; + handled = true; } // Check first whether the "clear search" button wants to deal with this. if(!handled && childrenHandleMouseUp(x, y, mask) != NULL) { - return TRUE; + return true; } if( mIsSelecting ) @@ -919,7 +919,7 @@ BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) setCursorAtLocalPos( x ); mSelectionEnd = getCursor(); - handled = TRUE; + handled = true; } if( handled ) @@ -1597,18 +1597,18 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask ) } -BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char) +bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) { if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL { - return FALSE; + return false; } - BOOL handled = FALSE; + bool handled = false; if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly) { - handled = TRUE; + handled = true; LLLineEditorRollback rollback( this ); @@ -2489,7 +2489,7 @@ void LLLineEditor::updatePreedit(const LLWString &preedit_string, mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); } -BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const { if (control) { @@ -2511,13 +2511,13 @@ BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect if (preedit_right_column < mScrollHPos) { // This should not occure... - return FALSE; + return false; } const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor()); if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column) { - return FALSE; + return false; } if (coord) @@ -2544,7 +2544,7 @@ BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); } - return TRUE; + return true; } void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index f983828d2b..cdb514deaa 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -122,14 +122,14 @@ public: virtual ~LLLineEditor(); // mousehandler overrides - /*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 handleDoubleClick(S32 x,S32 y,MASK mask); - /*virtual*/ BOOL handleMiddleMouseDown(S32 x,S32 y,MASK mask); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, 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 handleDoubleClick(S32 x,S32 y,MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x,S32 y,MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); - /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char); + /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); /*virtual*/ void onMouseCaptureLost(); // LLEditMenuHandler overrides @@ -319,7 +319,7 @@ public: const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position); virtual void markAsPreedit(S32 position, S32 length); virtual void getPreeditRange(S32 *position, S32 *length) const; - virtual BOOL getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; + virtual bool getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; virtual S32 getPreeditFontSize() const; virtual LLWString getPreeditString() const { return getWText(); } diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 583704418b..ceb78387ac 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -142,13 +142,13 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) return FALSE; } -BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) { LLButton::handleMouseDown(x, y, mask); toggleMenu(); - return TRUE; + return true; } void LLMenuButton::toggleMenu() diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index e42f8f53bd..b6ec4e2e35 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -65,7 +65,7 @@ public: boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); void hideMenu(); diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index cebca70b59..755ff5f4e3 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -229,14 +229,14 @@ BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) return FALSE; } -BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) +bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) { getWindow()->setCursor(UI_CURSOR_ARROW); - return TRUE; + return true; } //virtual -BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) { return LLUICtrl::handleRightMouseDown(x,y,mask); } @@ -254,14 +254,14 @@ void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask) } //virtual -BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) +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); + bool handled = handleMouseUp(x, y, mask); return handled; } return LLUICtrl::handleRightMouseUp(x,y,mask); @@ -453,26 +453,26 @@ BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) return FALSE; } -BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask) +bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask) { // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(FALSE); + LLMenuGL::setKeyboardMode(false); onCommit(); make_ui_sound("UISndClickRelease"); return LLView::handleMouseUp(x, y, mask); } -BOOL LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask) +bool LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask) { // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(FALSE); + LLMenuGL::setKeyboardMode(false); - setHighlight(TRUE); + setHighlight(true); return LLView::handleMouseDown(x, y, mask); } -BOOL LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) +bool LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) { // If the menu is scrollable let it handle the wheel event. return !getMenu()->isScrollable(); @@ -605,49 +605,49 @@ void LLMenuItemSeparatorGL::buildDrawLabel( void ) } } -BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) { LLMenuGL* parent_menu = getMenu(); if (y > getRect().getHeight() / 2) { // 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->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; + return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : false; } else { LLView* next_menu_item = parent_menu->findPrevSibling(this); - return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE; + return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : false; } } -BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) { LLMenuGL* parent_menu = getMenu(); if (y > getRect().getHeight() / 2) { LLView* prev_menu_item = parent_menu->findNextSibling(this); - return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; + return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : false; } else { LLView* next_menu_item = parent_menu->findPrevSibling(this); - return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE; + return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : false; } } -BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) +bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) { LLMenuGL* parent_menu = getMenu(); if (y > getRect().getHeight() / 2) { - parent_menu->highlightPrevItem(this, FALSE); - return FALSE; + parent_menu->highlightPrevItem(this, false); + return false; } else { - parent_menu->highlightNextItem(this, FALSE); - return FALSE; + parent_menu->highlightNextItem(this, false); + return false; } } @@ -663,7 +663,7 @@ class LLMenuItemVerticalSeparatorGL public: LLMenuItemVerticalSeparatorGL( void ); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } + virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; } }; LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) @@ -1019,14 +1019,14 @@ LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) } // virtual -BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) { // switch to mouse navigation mode LLMenuGL::setKeyboardMode(FALSE); onCommit(); make_ui_sound("UISndClickRelease"); - return TRUE; + return true; } bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const @@ -1363,8 +1363,8 @@ public: virtual BOOL isActive( void ) const; // LLView functionality - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); + virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); + virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); virtual void draw( void ); virtual BOOL handleKeyHere(KEY key, MASK mask); @@ -1490,10 +1490,10 @@ BOOL LLMenuItemBranchDownGL::isActive() const return isOpen(); } -BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) +bool LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) { // switch to mouse control mode - LLMenuGL::setKeyboardMode(FALSE); + LLMenuGL::setKeyboardMode(false); if (getVisible() && isOpen()) { @@ -1505,12 +1505,12 @@ BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) } make_ui_sound("UISndClick"); - return TRUE; + return true; } -BOOL LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask ) +bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask ) { - return TRUE; + return true; } @@ -3118,19 +3118,19 @@ BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) return FALSE; } -BOOL LLMenuGL::handleUnicodeCharHere( llwchar uni_char ) +bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char ) { if (jumpKeysActive()) { return handleJumpKey((KEY)uni_char); } - return FALSE; + return false; } -BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) +bool LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) { // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU - BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; + bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y); @@ -3161,7 +3161,7 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) // moving mouse always highlights new item if (mouse_delta_x != 0 || mouse_delta_y != 0) { - ((LLMenuItemGL*)viewp)->setHighlight(FALSE); + ((LLMenuItemGL*)viewp)->setHighlight(false); } } } @@ -3183,8 +3183,8 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) // moving mouse always highlights new item if (mouse_delta_x != 0 || mouse_delta_y != 0) { - ((LLMenuItemGL*)viewp)->setHighlight(TRUE); - LLMenuGL::setKeyboardMode(FALSE); + ((LLMenuItemGL*)viewp)->setHighlight(true); + LLMenuGL::setKeyboardMode(false); } mHasSelection = true; } @@ -3197,10 +3197,10 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) // 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; + return true; } -BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) +bool LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) { if (!mScrollable) return blockMouseEvent(x, y); @@ -3216,7 +3216,7 @@ BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) scrollItems(SD_UP); } - return TRUE; + return true; } @@ -3503,7 +3503,7 @@ BOOL LLMenuBarGL::handleJumpKey(KEY key) return TRUE; } -BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) { // clicks on menu bar closes existing menus from other contexts but leave // own menu open so that we get toggle behavior @@ -3515,7 +3515,7 @@ BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) return LLMenuGL::handleMouseDown(x, y, mask); } -BOOL LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask) { return LLMenuGL::handleMouseDown(x, y, mask); } @@ -3652,9 +3652,9 @@ BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu ) return success; } -BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) +bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; LLView* active_menu = NULL; BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; @@ -3690,14 +3690,14 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) viewp->pointInView(local_x, local_y) && viewp->handleHover(local_x, local_y, mask)) { - ((LLMenuItemGL*)viewp)->setHighlight(TRUE); + ((LLMenuItemGL*)viewp)->setHighlight(true); handled = TRUE; if (active_menu && active_menu != viewp) { ((LLMenuItemGL*)viewp)->onCommit(); - LLMenuGL::setKeyboardMode(FALSE); + LLMenuGL::setKeyboardMode(false); } - LLMenuGL::setKeyboardMode(FALSE); + LLMenuGL::setKeyboardMode(false); } } @@ -3711,7 +3711,7 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) S32 local_y = y - viewp->getRect().mBottom; if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) { - ((LLMenuItemGL*)viewp)->setHighlight(FALSE); + ((LLMenuItemGL*)viewp)->setHighlight(false); } } } @@ -3719,7 +3719,7 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) getWindow()->setCursor(UI_CURSOR_ARROW); - return TRUE; + return true; } ///============================================================================ @@ -3759,9 +3759,9 @@ void LLMenuHolderGL::draw() } } -BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) +bool LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) { - BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; if (!handled) { LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu(); @@ -3786,9 +3786,9 @@ BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) return handled; } -BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) +bool LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) { - BOOL handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL; + bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL; if (!handled) { // clicked off of menu, hide them all @@ -3799,7 +3799,7 @@ BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) // 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 ) +bool LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask ) { const S32 SLOP = 2; S32 spawn_dx = (x - sContextMenuSpawnPos.mX); @@ -3811,10 +3811,10 @@ BOOL LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask ) // so interpret the mouse-up as a single-click to show and leave on // screen sContextMenuSpawnPos.set(S32_MAX, S32_MAX); - return TRUE; + return true; } - BOOL handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL; + bool handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL; if (!handled) { // clicked off of menu, hide them all @@ -4296,36 +4296,36 @@ void LLContextMenu::hide() } -BOOL LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) +bool LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) { LLMenuGL::handleHover(x,y,mask); - BOOL handled = FALSE; + bool handled = false; LLMenuItemGL *item = getHighlightedItem(); if (item && item->getEnabled()) { getWindow()->setCursor(UI_CURSOR_ARROW); - handled = TRUE; + handled = true; if (item != mHoverItem) { if (mHoverItem) { - mHoverItem->setHighlight( FALSE ); + mHoverItem->setHighlight( false ); } mHoverItem = item; - mHoverItem->setHighlight( TRUE ); + mHoverItem->setHighlight( true ); } - mHoveredAnyItem = TRUE; + mHoveredAnyItem = true; } else { // clear out our selection if (mHoverItem) { - mHoverItem->setHighlight(FALSE); + mHoverItem->setHighlight(false); mHoverItem = NULL; } } @@ -4333,7 +4333,7 @@ BOOL LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) if( !handled && pointInView( x, y ) ) { getWindow()->setCursor(UI_CURSOR_ARROW); - handled = TRUE; + handled = true; } return handled; @@ -4342,9 +4342,9 @@ BOOL LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) // handleMouseDown and handleMouseUp are handled by LLMenuGL -BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // The click was somewhere within our rectangle LLMenuItemGL *item = getHighlightedItem(); @@ -4352,13 +4352,13 @@ BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) S32 local_x = x - getRect().mLeft; S32 local_y = y - getRect().mBottom; - BOOL clicked_in_menu = pointInView(local_x, local_y) ; + bool clicked_in_menu = pointInView(local_x, local_y) ; // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie if (clicked_in_menu) { // capture mouse cursor as if on initial menu show - handled = TRUE; + handled = true; } if (item) @@ -4367,14 +4367,14 @@ BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) // to make sure it's within the item's rectangle if (item->handleMouseDown( 0, 0, mask )) { - handled = TRUE; + handled = true; } } return handled; } -BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) +bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) { S32 local_x = x - getRect().mLeft; S32 local_y = y - getRect().mBottom; @@ -4382,12 +4382,12 @@ BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) if (!mHoveredAnyItem && !pointInView(local_x, local_y)) { sMenuContainer->hideMenus(); - return TRUE; + return true; } BOOL result = handleMouseUp( x, y, mask ); - mHoveredAnyItem = FALSE; + mHoveredAnyItem = false; return result; } diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 87e3f18ebc..88fb30fbb2 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -90,9 +90,9 @@ protected: public: // LLView overrides /*virtual*/ void onVisibilityChange(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); + /*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); @@ -163,9 +163,9 @@ public: // LLView Functionality 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 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 onMouseEnter(S32 x, S32 y, MASK mask); virtual void onMouseLeave(S32 x, S32 y, MASK mask); @@ -241,9 +241,9 @@ public: LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params()); /*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*/ 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 void buildDrawLabel(); @@ -442,9 +442,9 @@ public: void parseChildXML(LLXMLNodePtr child, LLView* parent); // LLView Functionality - /*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*/ 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, F32 alpha); /*virtual*/ void setVisible(BOOL visible); @@ -643,7 +643,7 @@ protected: public: virtual ~LLMenuItemBranchGL(); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); @@ -721,9 +721,9 @@ public: virtual void show (S32 x, S32 y, LLView* spawning_view = NULL); virtual void hide (); - 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 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 addChild (LLView* view, S32 tab_group = 0); @@ -788,8 +788,8 @@ public: /*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 handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); /*virtual*/ void draw(); /*virtual*/ BOOL jumpKeysActive(); @@ -798,7 +798,7 @@ public: virtual BOOL addSeparator(); // LLView Functionality - virtual BOOL handleHover( S32 x, S32 y, MASK mask ); + virtual bool handleHover( S32 x, S32 y, MASK mask ); // Returns x position of rightmost child, usually Help menu S32 getRightmostMenuEdge(); @@ -837,11 +837,11 @@ public: // LLView functionality virtual void draw(); - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); + 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 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(); } diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index 3e5978eb59..d3afbdb8ba 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -169,7 +169,7 @@ void LLModalDialog::setVisible( BOOL visible ) LLFloater::setVisible( visible ); } -BOOL LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask) { LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu(); if (popup_menu != NULL) @@ -197,10 +197,10 @@ BOOL LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask) } - return TRUE; + return true; } -BOOL LLModalDialog::handleHover(S32 x, S32 y, MASK mask) +bool LLModalDialog::handleHover(S32 x, S32 y, MASK mask) { if( childrenHandleHover(x, y, mask) == NULL ) { @@ -223,36 +223,36 @@ BOOL LLModalDialog::handleHover(S32 x, S32 y, MASK mask) } } - return TRUE; + return true; } -BOOL LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask) { childrenHandleMouseUp(x, y, mask); - return TRUE; + return true; } -BOOL LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks) { childrenHandleScrollWheel(x, y, clicks); - return TRUE; + return true; } -BOOL LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask) { if (!LLFloater::handleDoubleClick(x, y, mask)) { // Click outside the panel make_ui_sound("UISndInvalidOp"); } - return TRUE; + return true; } -BOOL LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask) { LLMenuGL::sMenuContainer->hideMenus(); childrenHandleRightMouseDown(x, y, mask); - return TRUE; + return true; } diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h index f81273b96a..1c7f86a17e 100644 --- a/indra/llui/llmodaldialog.h +++ b/indra/llui/llmodaldialog.h @@ -49,12 +49,12 @@ public: /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - /*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 handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, 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 handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); /*virtual*/ void setVisible(BOOL visible); diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index 604d246f12..577e1f0a27 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -498,7 +498,7 @@ void LLMultiSlider::clear() LLF32UICtrl::clear(); } -BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask) +bool LLMultiSlider::handleHover(S32 x, S32 y, MASK mask) { if( gFocusMgr.getMouseCapture() == this ) { @@ -531,12 +531,12 @@ BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask) getWindow()->setCursor(UI_CURSOR_ARROW); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; } - return TRUE; + return true; } -BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( gFocusMgr.getMouseCapture() == this ) { @@ -545,23 +545,23 @@ BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask) if (mMouseUpSignal) (*mMouseUpSignal)( this, LLSD() ); - handled = TRUE; + handled = true; make_ui_sound("UISndClickRelease"); } else { - handled = TRUE; + handled = true; } return handled; } -BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask) { // only do sticky-focus on non-chrome widgets if (!getIsChrome()) { - setFocus(TRUE); + setFocus(true); } if (mMouseDownSignal) (*mMouseDownSignal)( this, LLSD() ); @@ -611,7 +611,7 @@ BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask) } make_ui_sound("UISndClick"); - return TRUE; + return true; } BOOL LLMultiSlider::handleKeyHere(KEY key, MASK mask) diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h index 3cb4b760b0..b6eacd33f5 100644 --- a/indra/llui/llmultislider.h +++ b/indra/llui/llmultislider.h @@ -110,9 +110,9 @@ public: void deleteCurSlider() { deleteSlider(mCurSlider); } /*virtual*/ void clear() override; - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) override; - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask) override; /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; /*virtual*/ void draw() override; diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index 2c7e7ab13d..32ce5e5575 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -54,7 +54,7 @@ public: /*virtual*/ void setValue(const LLSD& value); /*virtual*/ BOOL postBuild(); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); LLSD getPayload() { return mPayload; } @@ -470,12 +470,12 @@ BOOL LLRadioCtrl::postBuild() return TRUE; } -BOOL LLRadioCtrl::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLRadioCtrl::handleMouseDown(S32 x, S32 y, MASK mask) { // Grab focus preemptively, before button takes mousecapture if (hasTabStop() && getEnabled()) { - focusFirstItem(FALSE, FALSE); + focusFirstItem(false, false); } else { diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp index 115c4e23be..c05a7dddf7 100644 --- a/indra/llui/llresizebar.cpp +++ b/indra/llui/llresizebar.cpp @@ -89,9 +89,9 @@ LLResizeBar::LLResizeBar(const LLResizeBar::Params& p) } } -BOOL LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask) { - if (!canResize()) return FALSE; + if (!canResize()) return false; // Route future Mouse messages here preemptively. (Release on mouse up.) // No handler needed for focus lost since this clas has no state that depends on it. @@ -101,31 +101,31 @@ BOOL LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask) mLastMouseScreenX = mDragLastScreenX; mLastMouseScreenY = mDragLastScreenY; - return TRUE; + return true; } -BOOL LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( hasMouseCapture() ) { // Release the mouse gFocusMgr.setMouseCapture( NULL ); - handled = TRUE; + handled = true; } else { - handled = TRUE; + handled = true; } return handled; } -BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask) +bool LLResizeBar::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) @@ -289,11 +289,11 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask) } } - handled = TRUE; + handled = true; } else { - handled = TRUE; + handled = true; } if( handled && canResize() ) @@ -320,7 +320,7 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask) return handled; } // end LLResizeBar::handleHover -BOOL LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask) { LLRect orig_rect = mResizingView->getRect(); LLRect scaled_rect = orig_rect; @@ -350,7 +350,7 @@ BOOL LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask) mResizingView->setShape(scaled_rect, true); } - return TRUE; + return true; } void LLResizeBar::setImagePanel(LLPanel * panelp) diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h index 20a2406484..71e3ec3094 100644 --- a/indra/llui/llresizebar.h +++ b/indra/llui/llresizebar.h @@ -53,10 +53,10 @@ protected: public: - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); void setResizeLimits( S32 min_size, S32 max_size ) { mMinSize = min_size; mMaxSize = max_size; } void setEnableSnapping(BOOL enable) { mSnappingEnabled = enable; } diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 13ef0fdb7f..b1f4a6c69d 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -76,12 +76,12 @@ LLResizeHandle::~LLResizeHandle() } -BOOL LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( pointInHandle(x, y) ) { - handled = TRUE; + handled = true; // Route future Mouse messages here preemptively. (Release on mouse up.) // No handler needed for focus lost since this clas has no state that depends on it. gFocusMgr.setMouseCapture( this ); @@ -95,28 +95,28 @@ BOOL LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask) } -BOOL LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( hasMouseCapture() ) { // Release the mouse gFocusMgr.setMouseCapture( NULL ); - handled = TRUE; + handled = true; } else if( pointInHandle(x, y) ) { - handled = TRUE; + handled = true; } return handled; } -BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) +bool LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) @@ -327,13 +327,13 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) } } - handled = TRUE; + handled = true; } else // don't have mouse capture { if( pointInHandle( x, y ) ) { - handled = TRUE; + handled = true; } } diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h index ae20ecaa77..d82934f75b 100644 --- a/indra/llui/llresizehandle.h +++ b/indra/llui/llresizehandle.h @@ -51,9 +51,9 @@ protected: friend class LLUICtrlFactory; public: virtual void draw(); - virtual BOOL handleHover(S32 x, S32 y, 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 handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; } diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 735e2d529e..2cc0644d90 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -238,10 +238,10 @@ void LLScrollbar::updateThumbRect() } } -BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask) { // Check children first - BOOL handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + bool handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL; if( !handled_by_child ) { if( mThumbRect.pointInRect(x,y) ) @@ -279,16 +279,16 @@ BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask) } } - return TRUE; + return true; } -BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) +bool LLScrollbar::handleHover(S32 x, S32 y, MASK mask) { // Note: we don't bother sending the event to the children (the arrow buttons) // because they'll capture the mouse whenever they need hover events. - BOOL handled = FALSE; + bool handled = false; if( hasMouseCapture() ) { S32 height = getRect().getHeight(); @@ -382,7 +382,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) getWindow()->setCursor(UI_CURSOR_ARROW); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - handled = TRUE; + handled = true; } else { @@ -394,26 +394,26 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) { getWindow()->setCursor(UI_CURSOR_ARROW); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = TRUE; + handled = true; } - mDocChanged = FALSE; + mDocChanged = false; return handled; } // end handleHover -BOOL LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = changeLine( clicks * mStepSize, TRUE ); + bool handled = changeLine( clicks * mStepSize, true ); return handled; } -BOOL LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks) +bool LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = FALSE; + bool handled = FALSE; if (LLScrollbar::HORIZONTAL == mOrientation) { - handled = changeLine(clicks * mStepSize, TRUE); + handled = changeLine(clicks * mStepSize, true); } return handled; } @@ -440,13 +440,13 @@ BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, return FALSE; } -BOOL LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( hasMouseCapture() ) { gFocusMgr.setMouseCapture( NULL ); - handled = TRUE; + handled = true; } else { @@ -457,7 +457,7 @@ BOOL LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask) return handled; } -BOOL LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask) { // just treat a double click as a second click return handleMouseDown(x, y, mask); diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 9be9d22db8..82607f2dd0 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -83,12 +83,12 @@ public: // Overrides from LLView 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 handleDoubleClick(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - virtual BOOL handleScrollHWheel(S32 x, S32 y, S32 clicks); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); + virtual bool handleScrollHWheel(S32 x, S32 y, S32 clicks); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string &tooltip_msg); diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index ad32f7186c..48b55a1d87 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -227,21 +227,21 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask) return FALSE; } -BOOL LLScrollContainer::handleUnicodeCharHere(llwchar uni_char) +bool LLScrollContainer::handleUnicodeCharHere(llwchar uni_char) { if (mScrolledView && mScrolledView->handleUnicodeCharHere(uni_char)) { - return TRUE; + return true; } - return FALSE; + return false; } -BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) +bool LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) { // 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; + return true; // When the vertical scrollbar is visible, scroll wheel // only affects vertical scrolling. It's confusing to have @@ -257,7 +257,7 @@ BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) updateScroll(); } // Always eat the event - return TRUE; + return true; } LLScrollbar* horizontal = mScrollbar[HORIZONTAL]; @@ -268,16 +268,16 @@ BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) && horizontal->handleScrollWheel( 0, 0, clicks ) ) { updateScroll(); - return TRUE; + return true; } - return FALSE; + return false; } -BOOL LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks) +bool LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks) { if (LLUICtrl::handleScrollHWheel(x,y,clicks)) { - return TRUE; + return true; } LLScrollbar* horizontal = mScrollbar[HORIZONTAL]; @@ -286,10 +286,10 @@ BOOL LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks) && horizontal->handleScrollHWheel( 0, 0, clicks ) ) { updateScroll(); - return TRUE; + return true; } - return FALSE; + return false; } BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index dacea2a987..2875f95526 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -107,9 +107,9 @@ public: // LLView functionality virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL handleUnicodeCharHere(llwchar uni_char); - virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); - virtual BOOL handleScrollHWheel( S32 x, S32 y, S32 clicks ); + virtual bool handleUnicodeCharHere(llwchar uni_char); + virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); + virtual bool handleScrollHWheel( S32 x, S32 y, S32 clicks ); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp index 82b0415624..3dff93af98 100644 --- a/indra/llui/llscrolllistcolumn.cpp +++ b/indra/llui/llscrolllistcolumn.cpp @@ -93,7 +93,7 @@ void LLScrollColumnHeader::draw() LLButton::draw(); } -BOOL LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) { if (canResize() && mResizeBar->getRect().pointInRect(x, y)) { @@ -107,7 +107,7 @@ BOOL LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) { onClick(LLSD()); } - return TRUE; + return true; } void LLScrollColumnHeader::onClick(const LLSD& data) diff --git a/indra/llui/llscrolllistcolumn.h b/indra/llui/llscrolllistcolumn.h index b4d4a6d05e..1699c3d4e9 100644 --- a/indra/llui/llscrolllistcolumn.h +++ b/indra/llui/llscrolllistcolumn.h @@ -50,7 +50,7 @@ public: ~LLScrollColumnHeader(); /*virtual*/ void draw(); - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); /*virtual*/ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding); /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 219667f766..56873ec11a 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1743,29 +1743,29 @@ void LLScrollListCtrl::setEnabled(BOOL enabled) mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize()); } -BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = FALSE; + bool handled = false; // Pretend the mouse is over the scrollbar handled = mScrollbar->handleScrollWheel( 0, 0, clicks ); if (mMouseWheelOpaque) { - return TRUE; + return true; } return handled; } -BOOL LLScrollListCtrl::handleScrollHWheel(S32 x, S32 y, S32 clicks) +bool LLScrollListCtrl::handleScrollHWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = FALSE; + bool handled = false; // Pretend the mouse is over the scrollbar handled = mScrollbar->handleScrollHWheel( 0, 0, clicks ); if (mMouseWheelOpaque) { - return TRUE; + return true; } return handled; @@ -1783,20 +1783,20 @@ LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index) return cell_rect; } -BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) +bool LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) { S32 column_index = getColumnIndexFromOffset(x); LLScrollListColumn* columnp = getColumn(column_index); - if (columnp == NULL) return FALSE; + if (columnp == NULL) return false; - BOOL handled = FALSE; + bool handled = false; // show tooltip for full name of hovered item if it has been truncated LLScrollListItem* hit_item = hitItem(x, y); if (hit_item) { LLScrollListCell* hit_cell = hit_item->getColumn(column_index); - if (!hit_cell) return FALSE; + if (!hit_cell) return false; if (hit_cell && hit_cell->isText() && hit_cell->needsToolTip()) @@ -1815,7 +1815,7 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) .delay_time(0.2f) .sticky_rect(sticky_rect)); } - handled = TRUE; + handled = true; } // otherwise, look for a tooltip associated with this column @@ -1934,14 +1934,14 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) } -BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMouseDown(x, y, mask) != NULL; + bool handled = childrenHandleMouseDown(x, y, mask) != NULL; if( !handled ) { // set keyboard focus first, in case click action wants to move focus elsewhere - setFocus(TRUE); + setFocus(true); // clear selection changed flag because user is starting a selection operation mSelectionChanged = false; @@ -1952,7 +1952,7 @@ BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) return TRUE; } -BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) { if (hasMouseCapture()) { @@ -1978,7 +1978,7 @@ BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) } // virtual -BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) { LLScrollListItem *item = hitItem(x, y); if (item) @@ -2035,7 +2035,7 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) } return LLUICtrl::handleRightMouseDown(x, y, mask); } - return FALSE; + return false; } void LLScrollListCtrl::showProfile(std::string id, bool is_group) @@ -2108,10 +2108,10 @@ void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group) LLUrlAction::copyURLToClipboard(slurl); } -BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) { //BOOL handled = FALSE; - BOOL handled = handleClick(x, y, mask); + bool handled = handleClick(x, y, mask); if (!handled) { @@ -2127,7 +2127,7 @@ BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) } } - return TRUE; + return true; } BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) @@ -2279,9 +2279,9 @@ S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index) } -BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) +bool LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) { - BOOL handled = FALSE; + bool handled = false; if (hasMouseCapture()) { @@ -2521,11 +2521,11 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) return handled; } -BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) +bool LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) { if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL { - return FALSE; + return false; } // perform incremental search based on keyboard input @@ -2538,7 +2538,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) // type ahead search is case insensitive uni_char = LLStringOps::toLower((llwchar)uni_char); - if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE)) + if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), false)) { // update search string only on successful match mNeedsScroll = true; @@ -2609,7 +2609,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) } } - return TRUE; + return true; } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 73b4fb036a..a121913579 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -337,16 +337,16 @@ public: // Overridden from LLView /*virtual*/ void draw(); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); - /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char); - /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleScrollHWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ void setEnabled(BOOL enabled); /*virtual*/ void setFocus( BOOL b ); /*virtual*/ void onFocusReceived(); diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index 62df5a2c38..e759e7716e 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -157,7 +157,7 @@ void LLSlider::setValueAndCommit(F32 value) } -BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask) +bool LLSlider::handleHover(S32 x, S32 y, MASK mask) { if( hasMouseCapture() ) { @@ -193,12 +193,12 @@ BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask) getWindow()->setCursor(UI_CURSOR_ARROW); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; } - return TRUE; + return true; } -BOOL LLSlider::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLSlider::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( hasMouseCapture() ) { @@ -207,23 +207,23 @@ BOOL LLSlider::handleMouseUp(S32 x, S32 y, MASK mask) if (mMouseUpSignal) (*mMouseUpSignal)( this, getValueF32() ); - handled = TRUE; + handled = true; make_ui_sound("UISndClickRelease"); } else { - handled = TRUE; + handled = true; } return handled; } -BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) { // only do sticky-focus on non-chrome widgets if (!getIsChrome()) { - setFocus(TRUE); + setFocus(true); } if (mMouseDownSignal) (*mMouseDownSignal)( this, getValueF32() ); @@ -253,7 +253,7 @@ BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) } make_ui_sound("UISndClick"); - return TRUE; + return true; } BOOL LLSlider::handleKeyHere(KEY key, MASK mask) @@ -277,13 +277,13 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask) return handled; } -BOOL LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks) { if ( mOrientation == VERTICAL ) { F32 new_val = getValueF32() - clicks * getIncrement(); setValueAndCommit(new_val); - return TRUE; + return true; } return LLF32UICtrl::handleScrollWheel(x,y,clicks); } diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index 484a5373b3..ad3df1da82 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -72,11 +72,11 @@ public: boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); virtual void draw(); private: diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index c411aafb1a..3a3ecb2d85 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -457,7 +457,7 @@ void LLSpinCtrl::reportInvalidData() make_ui_sound("UISndBadKeystroke"); } -BOOL LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) { if( clicks > 0 ) { @@ -472,7 +472,7 @@ BOOL LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) onUpBtn(getValue()); } - return TRUE; + return true; } BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask) diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index cab99c35bd..8d22693021 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -88,7 +88,7 @@ public: void forceEditorCommit(); // for commit on external button - virtual BOOL handleScrollWheel(S32 x,S32 y,S32 clicks); + virtual bool handleScrollWheel(S32 x,S32 y,S32 clicks); virtual BOOL handleKeyHere(KEY key, MASK mask); void onEditorCommit(const LLSD& data); diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp index 2449100952..1ddb15473a 100644 --- a/indra/llui/llstatbar.cpp +++ b/indra/llui/llstatbar.cpp @@ -209,7 +209,7 @@ LLStatBar::LLStatBar(const Params& p) setStat(p.stat); } -BOOL LLStatBar::handleHover(S32 x, S32 y, MASK mask) +bool LLStatBar::handleHover(S32 x, S32 y, MASK mask) { switch(mStatType) { @@ -228,38 +228,38 @@ BOOL LLStatBar::handleHover(S32 x, S32 y, MASK mask) default: break; } - return TRUE; + return true; } -BOOL LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = LLView::handleMouseDown(x, y, mask); + bool handled = LLView::handleMouseDown(x, y, mask); if (!handled) { if (mDisplayBar) { if (mDisplayHistory || mOrientation == HORIZONTAL) { - mDisplayBar = FALSE; - mDisplayHistory = FALSE; + mDisplayBar = false; + mDisplayHistory = false; } else { - mDisplayHistory = TRUE; + mDisplayHistory = true; } } else { - mDisplayBar = TRUE; + mDisplayBar = true; if (mOrientation == HORIZONTAL) { - mDisplayHistory = TRUE; + mDisplayHistory = true; } } LLView* parent = getParent(); - parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), FALSE); + parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), false); } - return TRUE; + return true; } template diff --git a/indra/llui/llstatbar.h b/indra/llui/llstatbar.h index 6b481ca68f..8c74aaf4ad 100644 --- a/indra/llui/llstatbar.h +++ b/indra/llui/llstatbar.h @@ -61,8 +61,8 @@ public: LLStatBar(const Params&); virtual void draw(); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); void setStat(const std::string& stat_name); diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index cb36f72f6e..512b2a9a30 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -541,11 +541,11 @@ void LLTabContainer::draw() // virtual -BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) +bool LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) { static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - BOOL handled = FALSE; - BOOL has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); + bool handled = false; + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); if (has_scroll_arrows) { @@ -617,10 +617,10 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) } // virtual -BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) +bool LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) { - BOOL handled = FALSE; - BOOL has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); + bool handled = false; + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); if (has_scroll_arrows) { @@ -663,10 +663,10 @@ BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) } // virtual -BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) +bool LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) { - BOOL handled = FALSE; - BOOL has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); + bool handled = false; + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); S32 local_x = x - getRect().mLeft; S32 local_y = y - getRect().mBottom; @@ -710,11 +710,11 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) { if (cur_panel) { - if (!cur_panel->focusFirstItem(FALSE)) + if (!cur_panel->focusFirstItem(false)) { // if nothing in the panel gets focus, make sure the new tab does // otherwise the last tab might keep focus - getTab(getCurrentPanelIndex())->mButton->setFocus(TRUE); + getTab(getCurrentPanelIndex())->mButton->setFocus(true); } } gFocusMgr.setMouseCapture(NULL); @@ -727,15 +727,15 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) } // virtual -BOOL LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask) +bool LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask) { static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - BOOL handled = LLPanel::handleToolTip( x, y, mask); + bool handled = LLPanel::handleToolTip( x, y, mask); if (!handled && getTabCount() > 0 && !getTabsHidden()) { LLTabTuple* firsttuple = getTab(0); - BOOL has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); LLRect clip; if (mIsVertical) { diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index aa4a08c4ff..1615d72758 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -139,10 +139,10 @@ public: /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); /*virtual*/ void draw(); - /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleMouseUp( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleMouseUp( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index e0697cb454..d53e7154ba 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1079,14 +1079,14 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert) needsReflow(reflow_start_index); } -BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) { // handle triple click if (!mTripleClickTimer.hasExpired()) { if (mSkipTripleClick) { - return TRUE; + return true; } S32 real_line = getLineNumFromDocIndex(mCursorPos, false); @@ -1114,26 +1114,26 @@ BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) if (line_start == -1) { - return TRUE; + return true; } mSelectionEnd = line_start; mSelectionStart = line_end; setCursorPos(line_start); - return TRUE; + return true; } LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleMouseDown(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleMouseDown(x, y, mask); } -BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (hasMouseCapture() && cur_segment && cur_segment->handleMouseUp(x, y, mask)) @@ -1146,57 +1146,57 @@ BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) // *TODO: send URL here? (*mURLClickSignal)(this, LLSD() ); } - return TRUE; + return true; } return LLUICtrl::handleMouseUp(x, y, mask); } -BOOL LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +bool LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleMiddleMouseDown(x, y, mask); } -BOOL LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask) +bool LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleMiddleMouseUp(x, y, mask); } -BOOL LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleRightMouseDown(x, y, mask); } -BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask) +bool LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleRightMouseUp(x, y, mask); } -BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask) { //Don't start triple click timer if user have clicked on scrollbar mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); @@ -1209,40 +1209,40 @@ BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask) LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleDoubleClick(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleDoubleClick(x, y, mask); } -BOOL LLTextBase::handleHover(S32 x, S32 y, MASK mask) +bool LLTextBase::handleHover(S32 x, S32 y, MASK mask) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleHover(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleHover(x, y, mask); } -BOOL LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks)) { - return TRUE; + return true; } return LLUICtrl::handleScrollWheel(x, y, clicks); } -BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask) +bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask) { LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleToolTip(x, y, mask)) { - return TRUE; + return true; } return LLUICtrl::handleToolTip(x, y, mask); @@ -3228,17 +3228,17 @@ void LLTextSegment::setToken( LLKeywordToken* token ) {} LLKeywordToken* LLTextSegment::getToken() const { return NULL; } void LLTextSegment::setToolTip( const std::string &msg ) {} void LLTextSegment::dump() const {} -BOOL LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return FALSE; } -BOOL LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return FALSE; } -BOOL LLTextSegment::handleScrollHWheel(S32 x, S32 y, S32 clicks) { return FALSE; } -BOOL LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return FALSE; } +bool LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return false; } +bool LLTextSegment::handleScrollHWheel(S32 x, S32 y, S32 clicks) { return false; } +bool LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return false; } const std::string& LLTextSegment::getName() const { return LLStringUtil::null; @@ -3246,7 +3246,7 @@ const std::string& LLTextSegment::getName() const void LLTextSegment::onMouseCaptureLost() {} void LLTextSegment::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {} void LLTextSegment::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {} -BOOL LLTextSegment::hasMouseCapture() { return FALSE; } +bool LLTextSegment::hasMouseCapture() { return false; } // // LLNormalTextSegment @@ -3364,7 +3364,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele return right_x; } -BOOL LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask) +bool LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask) { if (getStyle() && getStyle()->isLink()) { @@ -3372,13 +3372,13 @@ BOOL LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask) if(mEditor.getSegmentAtLocalPos(x, y, false) == this) { LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND); - return TRUE; + return true; } } - return FALSE; + return false; } -BOOL LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { if (getStyle() && getStyle()->isLink()) { @@ -3386,13 +3386,13 @@ BOOL LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) if(mEditor.getSegmentAtLocalPos(x, y, false) == this) { mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF()); - return TRUE; + return true; } } - return FALSE; + return false; } -BOOL LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { if (getStyle() && getStyle()->isLink()) { @@ -3400,14 +3400,14 @@ BOOL LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) if(mEditor.getSegmentAtLocalPos(x, y, false) == this) { // eat mouse down event on hyperlinks, so we get the mouse up - return TRUE; + return true; } } - return FALSE; + return false; } -BOOL LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { if (getStyle() && getStyle()->isLink()) { @@ -3423,14 +3423,14 @@ BOOL LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { LLUrlAction::openURLExternal(url); } - return TRUE; + return true; } } - return FALSE; + return false; } -BOOL LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +bool LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { std::string msg; // do we have a tooltip for a loaded keyword (for script editor)? @@ -3438,16 +3438,16 @@ BOOL LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { const LLWString& wmsg = mToken->getToolTip(); LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg)); - return TRUE; + return true; } // or do we have an explicitly set tooltip (e.g., for Urls) if (!mTooltip.empty()) { LLToolTipMgr::instance().show(mTooltip); - return TRUE; + return true; } - return FALSE; + return false; } void LLNormalTextSegment::setToolTip(const std::string& tooltip) @@ -3611,7 +3611,7 @@ F32 LLOnHoverChangeableTextSegment::draw(S32 start, S32 end, S32 selection_start } /*virtual*/ -BOOL LLOnHoverChangeableTextSegment::handleHover(S32 x, S32 y, MASK mask) +bool LLOnHoverChangeableTextSegment::handleHover(S32 x, S32 y, MASK mask) { mStyle = mEditor.getSkipLinkUnderline() ? mNormalStyle : mHoveredStyle; return LLNormalTextSegment::handleHover(x, y, mask); @@ -3786,15 +3786,15 @@ S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin return 0; } -BOOL LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +bool LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { if (!mTooltip.empty()) { LLToolTipMgr::instance().show(mTooltip); - return TRUE; + return true; } - return FALSE; + return false; } void LLImageTextSegment::setToolTip(const std::string& tooltip) diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 3611ab0499..1dd91eef32 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -94,22 +94,22 @@ public: virtual void dump() const; // LLMouseHandler interface - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMiddleMouseUp(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*/ BOOL handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleScrollHWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseUp(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*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ const std::string& getName() const; /*virtual*/ void onMouseCaptureLost(); /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; - /*virtual*/ BOOL hasMouseCapture(); + /*virtual*/ bool hasMouseCapture(); S32 getStart() const { return mStart; } void setStart(S32 start) { mStart = start; } @@ -142,11 +142,11 @@ public: /*virtual*/ void setToolTip(const std::string& tooltip); /*virtual*/ void dump() const; - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleToolTip(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 handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); protected: F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect); @@ -184,7 +184,7 @@ class LLOnHoverChangeableTextSegment : public LLNormalTextSegment public: LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ); /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); protected: // Style used for text when mouse pointer is over segment LLStyleConstSP mHoveredStyle; @@ -255,7 +255,7 @@ public: S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ void setToolTip(const std::string& tooltip); private: @@ -339,16 +339,16 @@ public: }; // LLMouseHandler interface - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMiddleMouseUp(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*/ BOOL handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseUp(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*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); // LLView interface /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 521dabf9d4..739b46bb07 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -52,9 +52,9 @@ LLTextBox::LLTextBox(const LLTextBox::Params& p) LLTextBox::~LLTextBox() {} -BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = LLTextBase::handleMouseDown(x, y, mask); + bool handled = LLTextBase::handleMouseDown(x, y, mask); if (getSoundFlags() & MOUSE_DOWN) { @@ -63,7 +63,7 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) if (!handled && mClickedCallback) { - handled = TRUE; + handled = true; } if (handled) @@ -75,9 +75,9 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) return handled; } -BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = LLTextBase::handleMouseUp(x, y, mask); + bool handled = LLTextBase::handleMouseUp(x, y, mask); if (getSoundFlags() & MOUSE_UP) { @@ -96,21 +96,21 @@ BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) if (mClickedCallback && !handled) { mClickedCallback(); - handled = TRUE; + handled = true; } } return handled; } -BOOL LLTextBox::handleHover(S32 x, S32 y, MASK mask) +bool LLTextBox::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = LLTextBase::handleHover(x, y, mask); + bool handled = LLTextBase::handleHover(x, y, mask); if (!handled && mClickedCallback && mShowCursorHand) { // Clickable text boxes change the cursor to a hand LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND); - return TRUE; + return true; } return handled; } diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index c3e3b61912..bf0348723b 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -48,9 +48,9 @@ protected: public: virtual ~LLTextBox(); - /*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 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*/ void setEnabled(BOOL enabled); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 3d2a426913..eabeeddcd4 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -668,15 +668,15 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p endSelection(); } -BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // set focus first, in case click callbacks want to change it // RN: do we really need to have a tab stop? if (hasTabStop()) { - setFocus( TRUE ); + setFocus( true ); } // Let scrollbar have first dibs @@ -689,7 +689,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) deselect(); } - BOOL start_select = TRUE; + bool start_select = true; if( start_select ) { // If we're not scrolling (handled by child), then we're selecting @@ -717,7 +717,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) } } - handled = TRUE; + handled = true; } // Delay cursor flashing @@ -730,11 +730,11 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) return handled; } -BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) { if (hasTabStop()) { - setFocus(TRUE); + setFocus(true); } bool show_menu = false; @@ -742,7 +742,7 @@ BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) // Prefer editor menu if it has selection. See EXT-6806. if (hasSelection()) { - S32 click_pos = getDocIndexFromLocalCoord(x, y, FALSE); + S32 click_pos = getDocIndexFromLocalCoord(x, y, false); if (click_pos > mSelectionStart && click_pos < mSelectionEnd) { show_menu = true; @@ -760,16 +760,16 @@ BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) showContextMenu(x, y); } - return TRUE; + return true; } -BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +bool LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { if (hasTabStop()) { - setFocus(TRUE); + setFocus(true); } if (!LLTextBase::handleMouseDown(x, y, mask)) @@ -781,13 +781,13 @@ BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) pastePrimary(); } } - return TRUE; + return true; } -BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) +bool LLTextEditor::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if(hasMouseCapture() ) { @@ -804,7 +804,7 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) } LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; getWindow()->setCursor(UI_CURSOR_IBEAM); - handled = TRUE; + handled = true; } if( !handled ) @@ -822,16 +822,16 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) if( !handled ) { getWindow()->setCursor(UI_CURSOR_IBEAM); - handled = TRUE; + handled = true; } return handled; } -BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // if I'm not currently selecting text if (!(mIsSelecting && hasMouseCapture())) @@ -857,7 +857,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) // take selection to 'primary' clipboard updatePrimary(); - handled = TRUE; + handled = true; } // Delay cursor flashing @@ -867,16 +867,16 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) { gFocusMgr.setMouseCapture( NULL ); - handled = TRUE; + handled = true; } return handled; } -BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // let scrollbar and text segments have first dibs handled = LLTextBase::handleDoubleClick(x, y, mask); @@ -914,7 +914,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // We don't want handleMouseUp() to "finish" the selection (and thereby // set mSelectionEnd to where the mouse is), so we finish the selection here. - mIsSelecting = FALSE; + mIsSelecting = false; // delay cursor flashing resetCursorBlink(); @@ -922,7 +922,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // take selection to 'primary' clipboard updatePrimary(); - handled = TRUE; + handled = true; } return handled; @@ -1825,14 +1825,14 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) } -BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) +bool LLTextEditor::handleUnicodeCharHere(llwchar uni_char) { if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL { - return FALSE; + return false; } - BOOL handled = FALSE; + bool handled = false; // Handle most keys only if the text editor is writeable. if( !mReadOnly ) @@ -1848,7 +1848,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) // Keys that add characters temporarily hide the cursor getWindow()->hideCursorUntilMouseMove(); - handled = TRUE; + handled = true; } if( handled ) @@ -2755,7 +2755,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, onKeyStroke(); } -BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +bool LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const { if (control) { @@ -2778,13 +2778,13 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos); if (query < preedit_left_position || query > preedit_right_position) { - return FALSE; + return false; } const S32 first_visible_line = getFirstVisibleLine(); if (query < getLineStart(first_visible_line)) { - return FALSE; + return false; } S32 current_line = first_visible_line; @@ -2845,7 +2845,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); } - return TRUE; + return true; } void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const @@ -2891,7 +2891,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length) mPreeditPositions[0] = position; mPreeditPositions[1] = position + length; mPreeditStandouts.resize(1); - mPreeditStandouts[0] = FALSE; + mPreeditStandouts[0] = false; } else { diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index f3939248c2..2b1986ff41 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -92,15 +92,15 @@ public: static S32 spacesPerTab(); // mousehandler overrides - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask ); - virtual BOOL handleMiddleMouseDown(S32 x,S32 y,MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleDoubleClick(S32 x, S32 y, MASK mask ); + virtual bool handleMiddleMouseDown(S32 x,S32 y,MASK mask); virtual BOOL handleKeyHere(KEY key, MASK mask ); - virtual BOOL handleUnicodeCharHere(llwchar uni_char); + virtual bool handleUnicodeCharHere(llwchar uni_char); virtual void onMouseCaptureLost(); @@ -259,7 +259,7 @@ protected: virtual void markAsPreedit(S32 position, S32 length); virtual void getPreeditRange(S32 *position, S32 *length) const; virtual void getSelectionRange(S32 *position, S32 *length) const; - virtual BOOL getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; + virtual bool getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; virtual S32 getPreeditFontSize() const; virtual LLWString getPreeditString() const { return getWText(); } // @@ -276,7 +276,7 @@ protected: LLWString mPreeditWString; LLWString mPreeditOverwrittenWString; std::vector mPreeditPositions; - std::vector mPreeditStandouts; + std::vector mPreeditStandouts; protected: LLUIColor mDefaultColor; diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp index 2707f7a15c..7925c1048d 100644 --- a/indra/llui/lltoolbar.cpp +++ b/indra/llui/lltoolbar.cpp @@ -406,11 +406,11 @@ bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash, bool forc return (command_button != NULL); } -BOOL LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask) { LLRect button_panel_rect; mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this); - BOOL handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y); + bool handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y); if (handle_it_here) { @@ -1122,16 +1122,16 @@ LLToolBarButton::~LLToolBarButton() delete mIsStartingSignal; } -BOOL LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask) { mMouseDownX = x; mMouseDownY = y; return LLButton::handleMouseDown(x, y, mask); } -BOOL LLToolBarButton::handleHover(S32 x, S32 y, MASK mask) +bool LLToolBarButton::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY); static LLCachedControl drag_threshold(*LLUI::getInstance()->mSettingGroups["config"], "DragAndDropDistanceThreshold", 3); @@ -1143,7 +1143,7 @@ BOOL LLToolBarButton::handleHover(S32 x, S32 y, MASK mask) { mStartDragItemCallback(x, y, this); mIsDragged = true; - handled = TRUE; + handled = true; } else { diff --git a/indra/llui/lltoolbar.h b/indra/llui/lltoolbar.h index 370941c787..3a4f2fb76e 100644 --- a/indra/llui/lltoolbar.h +++ b/indra/llui/lltoolbar.h @@ -62,8 +62,8 @@ public: LLToolBarButton(const Params& p); ~LLToolBarButton(); - BOOL handleMouseDown(S32 x, S32 y, MASK mask); - BOOL handleHover(S32 x, S32 y, MASK mask); + bool handleMouseDown(S32 x, S32 y, MASK mask); + bool handleHover(S32 x, S32 y, MASK mask); void reshape(S32 width, S32 height, BOOL called_from_parent = true); void setEnabled(BOOL enabled); @@ -215,7 +215,7 @@ public: // virtuals void draw(); void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + bool handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index a6552d4ff1..0c5a1adcb3 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -71,7 +71,7 @@ void LLToolTipView::draw() LLView::draw(); } -BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask) +bool LLToolTipView::handleHover(S32 x, S32 y, MASK mask) { static S32 last_x = x; static S32 last_y = y; @@ -89,7 +89,7 @@ BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask) return LLView::handleHover(x, y, mask); } -BOOL LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask) { LLToolTipMgr::instance().blockToolTips(); @@ -98,29 +98,29 @@ BOOL LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask) // If we are handling the mouse event menu holder // won't get a chance to close menus so do this here LLMenuGL::sMenuContainer->hideMenus(); - return TRUE; + return true; } - return FALSE; + return false; } -BOOL LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +bool LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { LLToolTipMgr::instance().blockToolTips(); return LLView::handleMiddleMouseDown(x, y, mask); } -BOOL LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask) { LLToolTipMgr::instance().blockToolTips(); return LLView::handleRightMouseDown(x, y, mask); } -BOOL LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks ) +bool LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks ) { LLToolTipMgr::instance().blockToolTips(); - return FALSE; + return false; } void LLToolTipView::drawStickyRect() @@ -341,7 +341,7 @@ void LLToolTip::setVisible(BOOL visible) } } -BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask) +bool LLToolTip::handleHover(S32 x, S32 y, MASK mask) { //mInfoButton->setFlashing(true); if(mInfoButton) @@ -352,7 +352,7 @@ BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask) { getWindow()->setCursor(UI_CURSOR_HAND); } - return TRUE; + return true; } void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask) diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h index 86943625ff..e95a1754d0 100644 --- a/indra/llui/lltooltip.h +++ b/indra/llui/lltooltip.h @@ -44,11 +44,11 @@ public: Params(); }; LLToolTipView(const LLToolTipView::Params&); - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks ); void drawStickyRect(); @@ -97,7 +97,7 @@ public: Params(); }; /*virtual*/ void draw(); - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); /*virtual*/ void setVisible(BOOL visible); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 21afcae7c3..a5a1f7ac1a 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -342,12 +342,12 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask) } //virtual -BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) { LL_DEBUGS() << "LLUICtrl::handleMouseDown calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; - BOOL handled = LLView::handleMouseDown(x,y,mask); + bool handled = LLView::handleMouseDown(x,y,mask); if (mMouseDownSignal) { @@ -362,12 +362,12 @@ BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) } //virtual -BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) { LL_DEBUGS() << "LLUICtrl::handleMouseUp calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; - BOOL handled = LLView::handleMouseUp(x,y,mask); + bool handled = LLView::handleMouseUp(x,y,mask); if (handled) { LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname()); } @@ -382,9 +382,9 @@ BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) } //virtual -BOOL LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = LLView::handleRightMouseDown(x,y,mask); + bool handled = LLView::handleRightMouseDown(x,y,mask); if (mRightMouseDownSignal) { (*mRightMouseDownSignal)(this,x,y,mask); @@ -393,9 +393,9 @@ BOOL LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) } //virtual -BOOL LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask) +bool LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = LLView::handleRightMouseUp(x,y,mask); + bool handled = LLView::handleRightMouseUp(x,y,mask); if(mRightMouseUpSignal) { (*mRightMouseUpSignal)(this,x,y,mask); @@ -403,9 +403,9 @@ BOOL LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask) return handled; } -BOOL LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL handled = LLView::handleDoubleClick(x, y, mask); + bool handled = LLView::handleDoubleClick(x, y, mask); if (mDoubleClickSignal) { (*mDoubleClickSignal)(this, x, y, mask); diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index f1b6746ea0..c5182fefe9 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -155,11 +155,11 @@ public: /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override; /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; /*virtual*/ BOOL canFocusChildren() const override; - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override; // From LLFocusableElement /*virtual*/ void setFocus( BOOL b ) override; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index da7868d804..4a6d10a790 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -683,7 +683,7 @@ void LLView::setSnappedTo(const LLView* snap_view) { } -BOOL LLView::handleHover(S32 x, S32 y, MASK mask) +bool LLView::handleHover(S32 x, S32 y, MASK mask) { return childrenHandleHover( x, y, mask ) != NULL; } @@ -897,9 +897,9 @@ F32 LLView::getTooltipTimeout() : tooltip_delay); } -BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) +bool LLView::handleToolTip(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // parents provide tooltips first, which are optionally // overridden by children, in case child is mouse_opaque @@ -916,14 +916,14 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) .sticky_rect(calcScreenRect()) .delay_time(getTooltipTimeout())); } - handled = TRUE; + handled = true; } // child tooltips will override our own LLView* child_handler = childrenHandleToolTip(x, y, mask); if (child_handler) { - handled = TRUE; + handled = true; } return handled; @@ -1044,9 +1044,9 @@ BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) } -BOOL LLView::handleUnicodeCharHere(llwchar uni_char ) +bool LLView::handleUnicodeCharHere(llwchar uni_char ) { - return FALSE; + return false; } @@ -1062,56 +1062,56 @@ void LLView::onMouseCaptureLost() { } -BOOL LLView::hasMouseCapture() +bool LLView::hasMouseCapture() { return gFocusMgr.getMouseCapture() == this; } -BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLView::handleMouseUp(S32 x, S32 y, MASK mask) { LLView* r = childrenHandleMouseUp( x, y, mask ); return (r!=NULL); } -BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLView::handleMouseDown(S32 x, S32 y, MASK mask) { LLView* r= childrenHandleMouseDown(x, y, mask ); return (r!=NULL); } -BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLView::handleDoubleClick(S32 x, S32 y, MASK mask) { return childrenHandleDoubleClick( x, y, mask ) != NULL; } -BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) { return childrenHandleScrollWheel( x, y, clicks ) != NULL; } -BOOL LLView::handleScrollHWheel(S32 x, S32 y, S32 clicks) +bool LLView::handleScrollHWheel(S32 x, S32 y, S32 clicks) { return childrenHandleScrollHWheel( x, y, clicks ) != NULL; } -BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) { return childrenHandleRightMouseDown( x, y, mask ) != NULL; } -BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) +bool LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) { return childrenHandleRightMouseUp( x, y, mask ) != NULL; } -BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +bool LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return childrenHandleMiddleMouseDown( x, y, mask ) != NULL; } -BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) +bool LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return childrenHandleMiddleMouseUp( x, y, mask ) != NULL; } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 7360fd0fb9..39eaf12913 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -421,21 +421,21 @@ public: // LLMouseHandler functions // Default behavior is to pass events to children - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleScrollHWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ const std::string& getName() const; /*virtual*/ void onMouseCaptureLost(); - /*virtual*/ BOOL hasMouseCapture(); + /*virtual*/ bool hasMouseCapture(); /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; @@ -514,7 +514,7 @@ public: //virtual BOOL addChildFromParam(const LLInitParam::BaseBlock& params) { return TRUE; } virtual BOOL handleKeyHere(KEY key, MASK mask); virtual BOOL handleKeyUpHere(KEY key, MASK mask); - virtual BOOL handleUnicodeCharHere(llwchar uni_char); + virtual bool handleUnicodeCharHere(llwchar uni_char); virtual void handleReshape(const LLRect& rect, bool by_user); virtual void dirtyRect(); diff --git a/indra/llui/llvirtualtrackball.cpp b/indra/llui/llvirtualtrackball.cpp index 6e0aef740d..6e86c4671f 100644 --- a/indra/llui/llvirtualtrackball.cpp +++ b/indra/llui/llvirtualtrackball.cpp @@ -384,7 +384,7 @@ void LLVirtualTrackball::getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 elevation *= RAD_TO_DEG; } -BOOL LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask) +bool LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask) { if (hasMouseCapture()) { @@ -413,7 +413,7 @@ BOOL LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask) { // set on click mode if (!pointInTouchCircle(x, y)) { - return TRUE; // don't drag outside the circle + return true; // don't drag outside the circle } F32 radius = mTouchArea->getRect().getWidth() / 2; @@ -453,10 +453,10 @@ BOOL LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask) mPrevY = y; onCommit(); } - return TRUE; + return true; } -BOOL LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask) { if (hasMouseCapture()) { @@ -468,7 +468,7 @@ BOOL LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask) return LLView::handleMouseUp(x, y, mask); } -BOOL LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask) { if (pointInTouchCircle(x, y)) { @@ -481,7 +481,7 @@ BOOL LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask) return LLView::handleMouseDown(x, y, mask); } -BOOL LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask) { if (pointInTouchCircle(x, y)) { diff --git a/indra/llui/llvirtualtrackball.h b/indra/llui/llvirtualtrackball.h index c7a893877b..574847de75 100644 --- a/indra/llui/llvirtualtrackball.h +++ b/indra/llui/llvirtualtrackball.h @@ -81,10 +81,10 @@ public: virtual ~LLVirtualTrackball(); /*virtual*/ BOOL postBuild(); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleKeyHere(KEY key, MASK mask); virtual void draw(); diff --git a/indra/llui/llxyvector.cpp b/indra/llui/llxyvector.cpp index d7ba243e1d..94dc72c86f 100644 --- a/indra/llui/llxyvector.cpp +++ b/indra/llui/llxyvector.cpp @@ -275,7 +275,7 @@ void LLXYVector::update() mYEntry->setValue(mValueY); } -BOOL LLXYVector::handleHover(S32 x, S32 y, MASK mask) +bool LLXYVector::handleHover(S32 x, S32 y, MASK mask) { if (hasMouseCapture()) { @@ -298,10 +298,10 @@ BOOL LLXYVector::handleHover(S32 x, S32 y, MASK mask) } } - return TRUE; + return true; } -BOOL LLXYVector::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLXYVector::handleMouseUp(S32 x, S32 y, MASK mask) { if (hasMouseCapture()) { @@ -311,7 +311,7 @@ BOOL LLXYVector::handleMouseUp(S32 x, S32 y, MASK mask) if (mTouchArea->getRect().pointInRect(x, y)) { - return TRUE; + return true; } else { @@ -319,7 +319,7 @@ BOOL LLXYVector::handleMouseUp(S32 x, S32 y, MASK mask) } } -BOOL LLXYVector::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLXYVector::handleMouseDown(S32 x, S32 y, MASK mask) { if (mTouchArea->getRect().pointInRect(x, y)) @@ -327,7 +327,7 @@ BOOL LLXYVector::handleMouseDown(S32 x, S32 y, MASK mask) gFocusMgr.setMouseCapture(this); make_ui_sound("UISndClick"); - return TRUE; + return true; } else { diff --git a/indra/llui/llxyvector.h b/indra/llui/llxyvector.h index bb3822dd26..1f6c73387e 100644 --- a/indra/llui/llxyvector.h +++ b/indra/llui/llxyvector.h @@ -68,9 +68,9 @@ public: virtual ~LLXYVector(); /*virtual*/ BOOL postBuild(); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); virtual void draw(); -- cgit v1.2.3 From 5486d87b566aabb43fa585de3ee86e5bc77391c9 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 19 Feb 2024 15:44:40 +0100 Subject: Change LLPreeditor::standouts_t to std::deque since std::vector since it's a specialization that does not necessarily behave like standard STL containers --- indra/llui/lllineeditor.cpp | 2 +- indra/llui/lltexteditor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index a1b9b5696c..9b320fdf97 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -2590,7 +2590,7 @@ void LLLineEditor::markAsPreedit(S32 position, S32 length) mPreeditPositions[0] = position; mPreeditPositions[1] = position + length; mPreeditStandouts.resize(1); - mPreeditStandouts[0] = FALSE; + mPreeditStandouts[0] = false; } else { diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 2b1986ff41..26d61b9502 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -276,7 +276,7 @@ protected: LLWString mPreeditWString; LLWString mPreeditOverwrittenWString; std::vector mPreeditPositions; - std::vector mPreeditStandouts; + LLPreeditor::standouts_t mPreeditStandouts; protected: LLUIColor mDefaultColor; -- cgit v1.2.3 From 8c16ec2b53153a10f40181e0e8108d24331451d4 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 13:57:07 +0100 Subject: Convert BOOL to bool in LLControlGroup and related classes --- indra/llui/llfloaterreg.cpp | 2 +- indra/llui/llstatview.cpp | 4 ++-- indra/llui/tests/llurlentry_test.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index f888d7ff68..62c26709d8 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -310,7 +310,7 @@ void LLFloaterReg::showInitialVisibleInstances() std::string controlname = getVisibilityControlName(name); if (LLFloater::getControlGroup()->controlExists(controlname)) { - BOOL isvis = LLFloater::getControlGroup()->getBOOL(controlname); + bool isvis = LLFloater::getControlGroup()->getBOOL(controlname); if (isvis) { showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true diff --git a/indra/llui/llstatview.cpp b/indra/llui/llstatview.cpp index bb4969c81f..4e74172777 100644 --- a/indra/llui/llstatview.cpp +++ b/indra/llui/llstatview.cpp @@ -40,7 +40,7 @@ LLStatView::LLStatView(const LLStatView::Params& p) : LLContainerView(p), mSetting(p.setting) { - BOOL isopen = getDisplayChildren(); + bool isopen = getDisplayChildren(); if (mSetting.length() > 0) { isopen = LLUI::getInstance()->mSettingGroups["config"]->getBOOL(mSetting); @@ -53,7 +53,7 @@ LLStatView::~LLStatView() // Children all cleaned up by default view destructor. if (mSetting.length() > 0) { - BOOL isopen = getDisplayChildren(); + bool isopen = getDisplayChildren(); LLUI::getInstance()->mSettingGroups["config"]->setBOOL(mSetting, isopen); } } diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 1a474cca90..2b44e61dea 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -58,7 +58,7 @@ typedef std::map settings_map_t; settings_map_t LLUI::sSettingGroups; -BOOL LLControlGroup::getBOOL(const std::string& name) +bool LLControlGroup::getBOOL(const std::string& name) { return false; } -- cgit v1.2.3 From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/llui/llaccordionctrl.cpp | 42 +-- indra/llui/llaccordionctrl.h | 10 +- indra/llui/llaccordionctrltab.cpp | 46 ++-- indra/llui/llaccordionctrltab.h | 8 +- indra/llui/llbadge.cpp | 4 +- indra/llui/llbadge.h | 2 +- indra/llui/llbutton.cpp | 56 ++-- indra/llui/llbutton.h | 30 +-- indra/llui/llchat.h | 4 +- indra/llui/llchatentry.cpp | 8 +- indra/llui/llchatentry.h | 2 +- indra/llui/llcheckboxctrl.cpp | 34 +-- indra/llui/llcheckboxctrl.h | 26 +- indra/llui/llcombobox.cpp | 140 +++++----- indra/llui/llcombobox.h | 64 ++--- indra/llui/llconsole.cpp | 2 +- indra/llui/llconsole.h | 2 +- indra/llui/llcontainerview.cpp | 16 +- indra/llui/llcontainerview.h | 20 +- indra/llui/llctrlselectioninterface.cpp | 8 +- indra/llui/llctrlselectioninterface.h | 24 +- indra/llui/lldockablefloater.cpp | 24 +- indra/llui/lldockablefloater.h | 8 +- indra/llui/lldraghandle.cpp | 12 +- indra/llui/lldraghandle.h | 12 +- indra/llui/lleditmenuhandler.h | 16 +- indra/llui/llfiltereditor.cpp | 2 +- indra/llui/llflatlistview.cpp | 24 +- indra/llui/llflatlistview.h | 10 +- indra/llui/llfloater.cpp | 226 ++++++++-------- indra/llui/llfloater.h | 118 ++++----- indra/llui/llfloaterreg.cpp | 24 +- indra/llui/llfloaterreg.h | 4 +- indra/llui/llflyoutbutton.cpp | 4 +- indra/llui/llflyoutbutton.h | 4 +- indra/llui/llfocusmgr.cpp | 48 ++-- indra/llui/llfocusmgr.h | 32 +-- indra/llui/llfolderview.cpp | 276 +++++++++---------- indra/llui/llfolderview.h | 64 ++--- indra/llui/llfolderviewitem.cpp | 252 +++++++++--------- indra/llui/llfolderviewitem.h | 100 +++---- indra/llui/llfolderviewmodel.h | 24 +- indra/llui/lliconctrl.cpp | 2 +- indra/llui/lliconctrl.h | 2 +- indra/llui/llkeywords.cpp | 4 +- indra/llui/lllayoutstack.cpp | 32 +-- indra/llui/lllayoutstack.h | 10 +- indra/llui/lllineeditor.cpp | 178 ++++++------- indra/llui/lllineeditor.h | 98 +++---- indra/llui/lllocalcliprect.cpp | 4 +- indra/llui/lllocalcliprect.h | 6 +- indra/llui/llmenubutton.cpp | 14 +- indra/llui/llmenubutton.h | 2 +- indra/llui/llmenugl.cpp | 454 ++++++++++++++++---------------- indra/llui/llmenugl.h | 192 +++++++------- indra/llui/llmodaldialog.cpp | 40 +-- indra/llui/llmodaldialog.h | 14 +- indra/llui/llmultifloater.cpp | 66 ++--- indra/llui/llmultifloater.h | 26 +- indra/llui/llmultislider.cpp | 28 +- indra/llui/llmultislider.h | 14 +- indra/llui/llmultisliderctrl.cpp | 26 +- indra/llui/llmultisliderctrl.h | 16 +- indra/llui/llnotifications.cpp | 4 +- indra/llui/llnotifications.h | 4 +- indra/llui/llpanel.cpp | 76 +++--- indra/llui/llpanel.h | 46 ++-- indra/llui/llradiogroup.cpp | 74 +++--- indra/llui/llradiogroup.h | 26 +- indra/llui/llresizebar.h | 4 +- indra/llui/llresizehandle.cpp | 6 +- indra/llui/llresizehandle.h | 2 +- indra/llui/llresmgr.cpp | 6 +- indra/llui/llscrollbar.cpp | 62 ++--- indra/llui/llscrollbar.h | 12 +- indra/llui/llscrollcontainer.cpp | 72 ++--- indra/llui/llscrollcontainer.h | 18 +- indra/llui/llscrollingpanellist.cpp | 4 +- indra/llui/llscrollingpanellist.h | 4 +- indra/llui/llscrolllistcell.cpp | 20 +- indra/llui/llscrolllistcell.h | 22 +- indra/llui/llscrolllistcolumn.cpp | 14 +- indra/llui/llscrolllistcolumn.h | 10 +- indra/llui/llscrolllistctrl.cpp | 226 ++++++++-------- indra/llui/llscrolllistctrl.h | 112 ++++---- indra/llui/llscrolllistitem.cpp | 8 +- indra/llui/llscrolllistitem.h | 18 +- indra/llui/llsearcheditor.cpp | 8 +- indra/llui/llsearcheditor.h | 8 +- indra/llui/llslider.cpp | 10 +- indra/llui/llslider.h | 8 +- indra/llui/llsliderctrl.cpp | 26 +- indra/llui/llsliderctrl.h | 18 +- indra/llui/llspinctrl.cpp | 42 +-- indra/llui/llspinctrl.h | 18 +- indra/llui/llstatgraph.cpp | 6 +- indra/llui/llstatgraph.h | 2 +- indra/llui/llstyle.cpp | 6 +- indra/llui/llstyle.h | 10 +- indra/llui/lltabcontainer.cpp | 164 ++++++------ indra/llui/lltabcontainer.h | 40 +-- indra/llui/lltextbase.cpp | 48 ++-- indra/llui/lltextbase.h | 36 +-- indra/llui/lltextbox.cpp | 10 +- indra/llui/lltextbox.h | 8 +- indra/llui/lltexteditor.cpp | 268 +++++++++---------- indra/llui/lltexteditor.h | 76 +++--- indra/llui/lltextparser.cpp | 8 +- indra/llui/lltextvalidate.cpp | 62 ++--- indra/llui/lltextvalidate.h | 2 +- indra/llui/lltimectrl.cpp | 12 +- indra/llui/lltimectrl.h | 4 +- indra/llui/lltoggleablemenu.cpp | 4 +- indra/llui/lltoggleablemenu.h | 2 +- indra/llui/lltoolbar.cpp | 38 +-- indra/llui/lltoolbar.h | 14 +- indra/llui/lltooltip.cpp | 6 +- indra/llui/lltooltip.h | 2 +- indra/llui/lltransutil.cpp | 2 +- indra/llui/llui.cpp | 2 +- indra/llui/lluictrl.cpp | 66 ++--- indra/llui/lluictrl.h | 52 ++-- indra/llui/lluictrlfactory.cpp | 2 +- indra/llui/llundo.cpp | 18 +- indra/llui/llundo.h | 10 +- indra/llui/llurlentry.cpp | 8 +- indra/llui/llurlentry.h | 2 +- indra/llui/llview.cpp | 178 ++++++------- indra/llui/llview.h | 154 +++++------ indra/llui/llviewborder.cpp | 10 +- indra/llui/llviewborder.h | 8 +- indra/llui/llviewereventrecorder.cpp | 2 +- indra/llui/llviewereventrecorder.h | 2 +- indra/llui/llviewquery.cpp | 10 +- indra/llui/llviewquery.h | 4 +- indra/llui/llvirtualtrackball.cpp | 18 +- indra/llui/llvirtualtrackball.h | 4 +- indra/llui/llxuiparser.cpp | 4 +- indra/llui/llxyvector.cpp | 6 +- indra/llui/llxyvector.h | 6 +- 140 files changed, 2722 insertions(+), 2722 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index a5417d054e..d88e1a1f11 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -103,7 +103,7 @@ void LLAccordionCtrl::draw() } //--------------------------------------------------------------------------------- -BOOL LLAccordionCtrl::postBuild() +bool LLAccordionCtrl::postBuild() { static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); @@ -127,7 +127,7 @@ BOOL LLAccordionCtrl::postBuild() mScrollbar = LLUICtrlFactory::create(sbparams); LLView::addChild(mScrollbar); - mScrollbar->setVisible(FALSE); + mScrollbar->setVisible(false); mScrollbar->setFollowsRight(); mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); @@ -167,7 +167,7 @@ BOOL LLAccordionCtrl::postBuild() updateNoTabsHelpTextVisibility(); - return TRUE; + return true; } @@ -179,7 +179,7 @@ LLAccordionCtrl::~LLAccordionCtrl() //--------------------------------------------------------------------------------- -void LLAccordionCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLAccordionCtrl::reshape(S32 width, S32 height, bool called_from_parent) { // adjust our rectangle LLRect rcLocal = getRect(); @@ -243,7 +243,7 @@ void LLAccordionCtrl::showScrollbar(S32 width, S32 height) { bool was_visible = mScrollbar->getVisible(); - mScrollbar->setVisible(TRUE); + mScrollbar->setVisible(true); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); @@ -265,9 +265,9 @@ void LLAccordionCtrl::showScrollbar(S32 width, S32 height) void LLAccordionCtrl::hideScrollbar(S32 width, S32 height) { - if (mScrollbar->getVisible() == FALSE) + if (mScrollbar->getVisible() == false) return; - mScrollbar->setVisible(FALSE); + mScrollbar->setVisible(false); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); @@ -391,7 +391,7 @@ void LLAccordionCtrl::updateNoTabsHelpTextVisibility() } } - mNoVisibleTabsHelpText->setVisible(visible_exists ? FALSE : TRUE); + mNoVisibleTabsHelpText->setVisible(visible_exists ? false : true); } void LLAccordionCtrl::arrangeSingle() @@ -407,7 +407,7 @@ void LLAccordionCtrl::arrangeSingle() { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->getVisible() == FALSE) // Skip hidden accordion tabs + if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) { @@ -421,7 +421,7 @@ void LLAccordionCtrl::arrangeSingle() { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->getVisible() == FALSE) // Skip hidden accordion tabs + if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) { @@ -469,7 +469,7 @@ void LLAccordionCtrl::arrangeMultiple() { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->getVisible() == FALSE) // Skip hidden accordion tabs + if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) @@ -561,15 +561,15 @@ bool LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) return false; } -BOOL LLAccordionCtrl::handleKeyHere(KEY key, MASK mask) +bool LLAccordionCtrl::handleKeyHere(KEY key, MASK mask) { if (mScrollbar->getVisible() && mScrollbar->handleKeyHere(key, mask)) - return TRUE; + return true; return LLPanel::handleKeyHere(key, mask); } -BOOL LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, +bool LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -577,17 +577,17 @@ BOOL LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, { // Scroll folder view if needed. Never accepts a drag or drop. *accept = ACCEPT_NO; - BOOL handled = autoScroll(x, y); + bool handled = autoScroll(x, y); if (!handled) { handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; } - return TRUE; + return true; } -BOOL LLAccordionCtrl::autoScroll(S32 x, S32 y) +bool LLAccordionCtrl::autoScroll(S32 x, S32 y) { static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); @@ -624,7 +624,7 @@ BOOL LLAccordionCtrl::autoScroll(S32 x, S32 y) } } - return scrolling ? TRUE : FALSE; + return scrolling ? true : false; } void LLAccordionCtrl::updateLayout(S32 width, S32 height) @@ -819,11 +819,11 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) } else if (info.has("child_visibility_change")) { - BOOL new_visibility = info["child_visibility_change"]; + bool new_visibility = info["child_visibility_change"]; if (new_visibility) { // there is at least one visible tab - mNoVisibleTabsHelpText->setVisible(FALSE); + mNoVisibleTabsHelpText->setVisible(false); } else { diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h index ba5a45759f..871c0a8f56 100644 --- a/indra/llui/llaccordionctrl.h +++ b/indra/llui/llaccordionctrl.h @@ -88,12 +88,12 @@ public: LLAccordionCtrl(); virtual ~LLAccordionCtrl(); - virtual BOOL postBuild(); + virtual bool postBuild(); virtual bool handleRightMouseDown ( S32 x, S32 y, MASK mask); virtual bool handleScrollWheel ( S32 x, S32 y, S32 clicks ); - virtual BOOL handleKeyHere (KEY key, MASK mask); - virtual BOOL handleDragAndDrop (S32 x, S32 y, MASK mask, BOOL drop, + virtual bool handleKeyHere (KEY key, MASK mask); + virtual bool handleDragAndDrop (S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -101,7 +101,7 @@ public: // // Call reshape after changing splitter's size - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); void addCollapsibleCtrl(LLView* view); void removeCollapsibleCtrl(LLView* view); @@ -159,7 +159,7 @@ private: void showScrollbar (S32 width, S32 height); void hideScrollbar (S32 width, S32 height); - BOOL autoScroll (S32 x, S32 y); + bool autoScroll (S32 x, S32 y); /** * An adaptor for LLTabComparator diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index c3787acec2..c8f75a1af2 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -64,9 +64,9 @@ public: virtual void draw(); - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - virtual BOOL postBuild(); + virtual bool postBuild(); std::string getTitle(); void setTitle(const std::string& title, const std::string& hl); @@ -79,8 +79,8 @@ public: virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual void onMouseLeave(S32 x, S32 y, MASK mask); - virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); + virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -150,9 +150,9 @@ LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader() { } -BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild() +bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild() { - return TRUE; + return true; } std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle() @@ -202,7 +202,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() S32 height = getRect().getHeight(); F32 alpha = getCurrentTransparency(); - gl_rect_2d(0, 0, width - 1, height - 1, mHeaderBGColor.get() % alpha, TRUE); + gl_rect_2d(0, 0, width - 1, height - 1, mHeaderBGColor.get() % alpha, true); LLAccordionCtrlTab* parent = dynamic_cast(getParent()); bool collapsible = parent && parent->getCollapsible(); @@ -245,7 +245,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() LLUICtrl::draw(); } -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */) { S32 header_height = mHeaderTextbox->getTextPixelHeight(); @@ -276,7 +276,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MA mAutoOpenTimer.stop(); } -BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, BOOL called_from_parent) +bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, bool called_from_parent) { if ((key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE) { @@ -286,8 +286,8 @@ BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, return LLUICtrl::handleKey(key, mask, called_from_parent); } -BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, +bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -303,7 +303,7 @@ BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 { parent->changeOpenClose(false); mAutoOpenTimer.stop(); - return TRUE; + return true; } } else @@ -380,7 +380,7 @@ LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p) LLFocusableElement::setFocusLostCallback(boost::bind(&LLAccordionCtrlTab::deselectOnFocusLost, this)); } - reshape(100, 200,FALSE); + reshape(100, 200,false); } LLAccordionCtrlTab::~LLAccordionCtrlTab() @@ -407,11 +407,11 @@ void LLAccordionCtrlTab::setDisplayChildren(bool display) else { if (mScrollbar) - mScrollbar->setVisible(FALSE); + mScrollbar->setVisible(false); } } -void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) +void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent /* = true */) { LLRect headerRect; @@ -439,14 +439,14 @@ void LLAccordionCtrlTab::changeOpenClose(bool is_open) mExpandedHeight = getRect().getHeight(); setDisplayChildren(!is_open); - reshape(getRect().getWidth(), getRect().getHeight(), FALSE); + reshape(getRect().getWidth(), getRect().getHeight(), false); if (mCommitSignal) { (*mCommitSignal)(this, getDisplayChildren()); } } -void LLAccordionCtrlTab::onVisibilityChange(BOOL new_visibility) +void LLAccordionCtrlTab::onVisibilityChange(bool new_visibility) { LLUICtrl::onVisibilityChange(new_visibility); @@ -643,14 +643,14 @@ void LLAccordionCtrlTab::setHeaderVisible(bool value) if (mHeader) { - mHeader->setVisible(value ? TRUE : FALSE); + mHeader->setVisible(value ? true : false); } - reshape(getRect().getWidth(), getRect().getHeight(), FALSE); + reshape(getRect().getWidth(), getRect().getHeight(), false); }; //virtual -BOOL LLAccordionCtrlTab::postBuild() +bool LLAccordionCtrlTab::postBuild() { if (mHeader) { @@ -812,7 +812,7 @@ S32 LLAccordionCtrlTab::notify(const LLSD& info) return 0; } -BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) +bool LLAccordionCtrlTab::handleKey(KEY key, MASK mask, bool called_from_parent) { if (!mHeader->hasFocus()) return LLUICtrl::handleKey(key, mask, called_from_parent); @@ -992,10 +992,10 @@ void LLAccordionCtrlTab::hideScrollbar(const LLRect& child_rect) if (!mContainerPanel || !mScrollbar) return; - if (mScrollbar->getVisible() == FALSE) + if (mScrollbar->getVisible() == false) return; - mScrollbar->setVisible(FALSE); + mScrollbar->setVisible(false); mScrollbar->setDocPos(0); //shrink child panel diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index 161f5c6361..8a1d90feac 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -134,7 +134,7 @@ public: void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close;}; bool canOpenClose() const { return mCanOpenClose; }; - virtual BOOL postBuild(); + virtual bool postBuild(); S32 notifyParent(const LLSD& info); S32 notify(const LLSD& info); @@ -153,19 +153,19 @@ protected: public: // Call reshape after changing size - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); /** * Raises notifyParent event with "child_visibility_change" = new_visibility */ - void onVisibilityChange(BOOL new_visibility); + void onVisibilityChange(bool new_visibility); virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); // Changes expand/collapse state and triggers expand/collapse callbacks virtual bool handleMouseDown(S32 x, S32 y, MASK mask); virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); virtual bool handleToolTip(S32 x, S32 y, MASK mask); virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); diff --git a/indra/llui/llbadge.cpp b/indra/llui/llbadge.cpp index 589b75ab5b..f404b2e03b 100644 --- a/indra/llui/llbadge.cpp +++ b/indra/llui/llbadge.cpp @@ -36,7 +36,7 @@ static LLDefaultChildRegistry::Register r("badge"); static const S32 BADGE_OFFSET_NOT_SPECIFIED = 0x7FFFFFFF; // Compiler optimization, generate extern template -template class LLBadge* LLView::getChild(const std::string& name, BOOL recurse) const; +template class LLBadge* LLView::getChild(const std::string& name, bool recurse) const; LLBadge::Params::Params() @@ -237,7 +237,7 @@ void LLBadge::draw() S32 badge_char_length = S32_MAX; S32 badge_pixel_length = S32_MAX; F32 *right_position_out = NULL; - BOOL do_not_use_ellipses = false; + bool do_not_use_ellipses = false; F32 badge_width = (2.0f * mPaddingHoriz) + mGLFont->getWidthF32(badge_label_wstring.c_str(), badge_label_begin_offset, badge_char_length); diff --git a/indra/llui/llbadge.h b/indra/llui/llbadge.h index 55f92e6e34..d6d74bcd0b 100644 --- a/indra/llui/llbadge.h +++ b/indra/llui/llbadge.h @@ -171,7 +171,7 @@ private: // Build time optimization, generate once in .cpp file #ifndef LLBADGE_CPP -extern template class LLBadge* LLView::getChild(const std::string& name, BOOL recurse) const; +extern template class LLBadge* LLView::getChild(const std::string& name, bool recurse) const; #endif #endif // LL_LLBADGE_H diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index d8589444fb..b0737c8238 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -56,7 +56,7 @@ static LLDefaultChildRegistry::Register r("button"); // Compiler optimization, generate extern template template class LLButton* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; // globals loaded from settings.xml S32 LLBUTTON_H_PAD = 0; @@ -122,10 +122,10 @@ LLButton::LLButton(const LLButton::Params& p) LLBadgeOwner(getHandle()), mMouseDownFrame(0), mMouseHeldDownCount(0), - mBorderEnabled( FALSE ), - mFlashing( FALSE ), + mBorderEnabled( false ), + mFlashing( false ), mCurGlowStrength(0.f), - mNeedsHighlight(FALSE), + mNeedsHighlight(false), mUnselectedLabel(p.label()), mSelectedLabel(p.label_selected()), mGLFont(p.font), @@ -167,7 +167,7 @@ LLButton::LLButton(const LLButton::Params& p) mHoverGlowStrength(p.hover_glow_amount), mCommitOnReturn(p.commit_on_return), mCommitOnCaptureLost(p.commit_on_capture_lost), - mFadeWhenDisabled(FALSE), + mFadeWhenDisabled(false), mForcePressedState(false), mDisplayPressedState(p.display_pressed_state), mLastDrawCharsCount(0), @@ -220,7 +220,7 @@ LLButton::LLButton(const LLButton::Params& p) if (p.image_disabled() == default_params.image_disabled() ) { mImageDisabled = p.image_unselected; - mFadeWhenDisabled = TRUE; + mFadeWhenDisabled = true; } if (p.image_pressed_selected == default_params.image_pressed_selected) @@ -236,7 +236,7 @@ LLButton::LLButton(const LLButton::Params& p) if (p.image_disabled_selected() == default_params.image_disabled_selected()) { mImageDisabledSelected = p.image_selected; - mFadeWhenDisabled = TRUE; + mFadeWhenDisabled = true; } if (p.image_pressed == default_params.image_pressed) @@ -384,7 +384,7 @@ boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, return setHeldDownCallback(boost::bind(cb, data)); } -BOOL LLButton::postBuild() +bool LLButton::postBuild() { autoResize(); @@ -411,9 +411,9 @@ bool LLButton::handleUnicodeCharHere(llwchar uni_char) return handled; } -BOOL LLButton::handleKeyHere(KEY key, MASK mask ) +bool LLButton::handleKeyHere(KEY key, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) { if (mIsToggle) @@ -421,7 +421,7 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask ) toggleState(); } - handled = TRUE; + handled = true; LLUICtrl::onCommit(); } @@ -579,7 +579,7 @@ void LLButton::onMouseLeave(S32 x, S32 y, MASK mask) { LLUICtrl::onMouseLeave(x, y, mask); - mNeedsHighlight = FALSE; + mNeedsHighlight = false; } void LLButton::setHighlight(bool b) @@ -630,7 +630,7 @@ void LLButton::draw() static LLCachedControl sEnableButtonFlashing(*LLUI::getInstance()->mSettingGroups["config"], "EnableButtonFlashing", true); F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); - bool pressed_by_keyboard = FALSE; + bool pressed_by_keyboard = false; if (hasFocus()) { pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); @@ -652,7 +652,7 @@ void LLButton::draw() || mForcePressedState; bool selected = getToggleState(); - bool use_glow_effect = FALSE; + bool use_glow_effect = false; LLColor4 highlighting_color = LLColor4::white; LLColor4 glow_color = LLColor4::white; LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; @@ -684,7 +684,7 @@ void LLButton::draw() else { imagep = mImageSelected; - use_glow_effect = TRUE; + use_glow_effect = true; } } else @@ -696,7 +696,7 @@ void LLButton::draw() else { imagep = mImageUnselected; - use_glow_effect = TRUE; + use_glow_effect = true; } } } @@ -738,7 +738,7 @@ void LLButton::draw() if (mFlashingTimer) { LLColor4 flash_color = mFlashBgColor.get(); - use_glow_effect = TRUE; + use_glow_effect = true; glow_type = LLRender::BT_ALPHA; // blend the glow if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress()) @@ -759,7 +759,7 @@ void LLButton::draw() if (mNeedsHighlight && !imagep) { - use_glow_effect = TRUE; + use_glow_effect = true; } // Figure out appropriate color for the text @@ -847,7 +847,7 @@ void LLButton::draw() // no image LL_DEBUGS() << "No image for button " << getName() << LL_ENDL; // draw it in pink so we can find it - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, false); } // let overlay image and text play well together @@ -981,12 +981,12 @@ void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size) } } -BOOL LLButton::getToggleState() const +bool LLButton::getToggleState() const { return getValue().asBoolean(); } -void LLButton::setToggleState(BOOL b) +void LLButton::setToggleState(bool b) { if( b != getToggleState() ) { @@ -1013,7 +1013,7 @@ void LLButton::setFlashing(bool b, bool force_flashing/* = false */) } } -BOOL LLButton::toggleState() +bool LLButton::toggleState() { bool flipped = ! getToggleState(); setToggleState(flipped); @@ -1028,11 +1028,11 @@ void LLButton::setLabel( const LLStringExplicit& label ) } //virtual -BOOL LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text ) { mUnselectedLabel.setArg(key, text); mSelectedLabel.setArg(key, text); - return TRUE; + return true; } void LLButton::setLabelUnselected( const LLStringExplicit& label ) @@ -1137,14 +1137,14 @@ void LLButton::setImageDisabled(LLPointer image) { mImageDisabled = image; mDisabledImageColor = mImageColor; - mFadeWhenDisabled = TRUE; + mFadeWhenDisabled = true; } void LLButton::setImageDisabledSelected(LLPointer image) { mImageDisabledSelected = image; mDisabledImageColor = mImageColor; - mFadeWhenDisabled = TRUE; + mFadeWhenDisabled = true; } void LLButton::setImagePressed(LLPointer image) @@ -1237,11 +1237,11 @@ void LLButton::addImageAttributeToXML(LLXMLNodePtr node, { if( !image_name.empty() ) { - node->createChild(xml_tag_name.c_str(), TRUE)->setStringValue(image_name); + 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); + node->createChild((xml_tag_name + "_id").c_str(), true)->setUUIDValue(image_id); } } diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 0c99f6a343..d3cdad874f 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -156,7 +156,7 @@ public: void addImageAttributeToXML(LLXMLNodePtr node, const std::string& imageName, const LLUUID& imageID,const std::string& xmlTagName) const; virtual bool handleUnicodeCharHere(llwchar uni_char); - virtual BOOL handleKeyHere(KEY key, MASK mask); + 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); @@ -164,7 +164,7 @@ public: virtual bool handleRightMouseUp(S32 x, S32 y, MASK mask); virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); virtual void draw(); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); virtual void onMouseLeave(S32 x, S32 y, MASK mask); virtual void onMouseCaptureLost(); @@ -173,7 +173,7 @@ public: void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; } void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; } - void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; } + void setUseEllipses( bool use_ellipses ) { mUseEllipses = use_ellipses; } boost::signals2::connection setClickedCallback(const CommitCallbackParam& cb); @@ -198,13 +198,13 @@ public: F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); } - BOOL toggleState(); - BOOL getToggleState() const; - void setToggleState(BOOL b); + bool toggleState(); + bool getToggleState() const; + void setToggleState(bool b); void setHighlight(bool b); void setFlashing( bool b, bool force_flashing = false ); - BOOL getFlashing() const { return mFlashing; } + bool getFlashing() const { return mFlashing; } LLFlashTimer* getFlashTimer() {return mFlashingTimer;} void setFlashColor(const LLUIColor &color) { mFlashBgColor = color; }; @@ -239,7 +239,7 @@ public: 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 ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); void setLabelUnselected(const LLStringExplicit& label); void setLabelSelected(const LLStringExplicit& label); void setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; } @@ -253,12 +253,12 @@ public: bool labelIsTruncated() const; const LLUIString& getCurrentLabel() const; - void setScaleImage(BOOL scale) { mScaleImage = scale; } - BOOL getScaleImage() const { return mScaleImage; } + void setScaleImage(bool scale) { mScaleImage = scale; } + bool getScaleImage() const { return mScaleImage; } - void setDropShadowedText(BOOL b) { mDropShadowedText = b; } + void setDropShadowedText(bool b) { mDropShadowedText = b; } - void setBorderEnabled(BOOL b) { mBorderEnabled = b; } + void setBorderEnabled(bool b) { mBorderEnabled = b; } void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; } @@ -271,8 +271,8 @@ public: void setImageFlash(LLPointer image); void setImagePressed(LLPointer image); - void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; } - BOOL getCommitOnReturn() const { return mCommitOnReturn; } + void setCommitOnReturn(bool commit) { mCommitOnReturn = commit; } + bool getCommitOnReturn() const { return mCommitOnReturn; } static void onHeldDown(void *userdata); // to be called by gIdleCallbacks static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname); @@ -395,7 +395,7 @@ protected: // Build time optimization, generate once in .cpp file #ifndef LLBUTTON_CPP extern template class LLButton* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif #endif // LL_LLBUTTON_H diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h index b4fd5f60aa..102889867e 100644 --- a/indra/llui/llchat.h +++ b/indra/llui/llchat.h @@ -83,7 +83,7 @@ public: mSourceType(CHAT_SOURCE_AGENT), mChatType(CHAT_TYPE_NORMAL), mAudible(CHAT_AUDIBLE_FULLY), - mMuted(FALSE), + mMuted(false), mTime(0.0), mTimeStr(), mPosAgent(), @@ -100,7 +100,7 @@ public: EChatSourceType mSourceType; EChatType mChatType; EChatAudible mAudible; - BOOL mMuted; // pass muted chat to maintain list of chatters + bool mMuted; // pass muted chat to maintain list of chatters F64 mTime; // viewer only, seconds from viewer start std::string mTimeStr; LLVector3 mPosAgent; diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp index c506576126..e7b7874b4b 100644 --- a/indra/llui/llchatentry.cpp +++ b/indra/llui/llchatentry.cpp @@ -176,9 +176,9 @@ void LLChatEntry::onFocusLost() LLUICtrl::onFocusLost(); } -BOOL LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) +bool LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) { - BOOL handled = FALSE; + bool handled = false; LLTextEditor::handleSpecialKey(key, mask); @@ -203,7 +203,7 @@ BOOL LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -227,7 +227,7 @@ BOOL LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h index 3f13691a30..06d9d462b4 100644 --- a/indra/llui/llchatentry.h +++ b/indra/llui/llchatentry.h @@ -84,7 +84,7 @@ private: */ void updateHistory(); - BOOL handleSpecialKey(const KEY key, const MASK mask); + bool handleSpecialKey(const KEY key, const MASK mask); // Fired when text height expanded to mExpandLinesCount diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 362fe0c19e..171ef1b51b 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -45,7 +45,7 @@ static LLDefaultChildRegistry::Register r("check_box"); // Compiler optimization, generate extern template template class LLCheckBoxCtrl* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; void LLCheckBoxCtrl::WordWrap::declareValues() { @@ -77,7 +77,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) static LLUICachedControl llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0); // must be big enough to hold all children - setUseBoundingRect(TRUE); + setUseBoundingRect(true); // *HACK Get rid of this with SL-55508... // this allows blank check boxes and radio boxes for now @@ -160,13 +160,13 @@ void LLCheckBoxCtrl::onCommit() { if( getEnabled() ) { - setTentative(FALSE); + setTentative(false); setControlValue(getValue()); LLUICtrl::onCommit(); } } -void LLCheckBoxCtrl::setEnabled(BOOL b) +void LLCheckBoxCtrl::setEnabled(bool b) { LLView::setEnabled(b); @@ -182,10 +182,10 @@ void LLCheckBoxCtrl::setEnabled(BOOL b) void LLCheckBoxCtrl::clear() { - setValue( FALSE ); + setValue( false ); } -void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLCheckBoxCtrl::reshape(S32 width, S32 height, bool called_from_parent) { LLRect rect = getRect(); S32 delta_width = width - rect.getWidth(); @@ -205,10 +205,10 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) LLRect label_rect = mLabel->getRect(); S32 new_width = rect.getWidth() - label_rect.mLeft; - mLabel->reshape(new_width, label_rect.getHeight(), TRUE); + mLabel->reshape(new_width, label_rect.getHeight(), true); S32 label_top = label_rect.mTop; - mLabel->reshapeToFitText(TRUE); + mLabel->reshapeToFitText(true); label_rect = mLabel->getRect(); if (label_top != label_rect.mTop && mWordWrap == WRAP_DOWN) @@ -247,13 +247,13 @@ LLSD LLCheckBoxCtrl::getValue() const } //virtual -void LLCheckBoxCtrl::setTentative(BOOL b) +void LLCheckBoxCtrl::setTentative(bool b) { mButton->setTentative(b); } //virtual -BOOL LLCheckBoxCtrl::getTentative() const +bool LLCheckBoxCtrl::getTentative() const { return mButton->getTentative(); } @@ -261,7 +261,7 @@ BOOL LLCheckBoxCtrl::getTentative() const void LLCheckBoxCtrl::setLabel( const LLStringExplicit& label ) { mLabel->setText( label ); - reshape(getRect().getWidth(), getRect().getHeight(), FALSE); + reshape(getRect().getWidth(), getRect().getHeight(), false); } std::string LLCheckBoxCtrl::getLabel() const @@ -269,10 +269,10 @@ std::string LLCheckBoxCtrl::getLabel() const return mLabel->getText(); } -BOOL LLCheckBoxCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLCheckBoxCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) { - BOOL res = mLabel->setTextArg(key, text); - reshape(getRect().getWidth(), getRect().getHeight(), FALSE); + bool res = mLabel->setTextArg(key, text); + reshape(getRect().getWidth(), getRect().getHeight(), false); return res; } @@ -283,14 +283,14 @@ void LLCheckBoxCtrl::setControlName(const std::string& control_name, LLView* con } -// virtual Returns TRUE if the user has modified this control. -BOOL LLCheckBoxCtrl::isDirty() const +// virtual Returns true if the user has modified this control. +bool LLCheckBoxCtrl::isDirty() const { if ( mButton ) { return mButton->isDirty(); } - return FALSE; // Shouldn't get here + return false; // Shouldn't get here } diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h index eb5bd5b6da..71b7d27629 100644 --- a/indra/llui/llcheckboxctrl.h +++ b/indra/llui/llcheckboxctrl.h @@ -36,8 +36,8 @@ // Constants // -const BOOL RADIO_STYLE = TRUE; -const BOOL CHECK_STYLE = FALSE; +const bool RADIO_STYLE = true; +const bool CHECK_STYLE = false; // // Classes @@ -87,28 +87,28 @@ protected: public: // LLView interface - virtual void setEnabled( BOOL b ); + virtual void setEnabled( bool b ); - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); // LLUICtrl interface virtual void setValue(const LLSD& value ); virtual LLSD getValue() const; - BOOL get() { return (BOOL)getValue().asBoolean(); } - void set(BOOL value) { setValue(value); } + bool get() { return (bool)getValue().asBoolean(); } + void set(bool value) { setValue(value); } - virtual void setTentative(BOOL b); - virtual BOOL getTentative() const; + virtual void setTentative(bool b); + virtual bool getTentative() const; - virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); virtual void clear(); virtual void onCommit(); // LLCheckBoxCtrl interface - virtual BOOL toggle() { return mButton->toggleState(); } // returns new state + virtual bool toggle() { return mButton->toggleState(); } // returns new state - void setBtnFocus() { mButton->setFocus(TRUE); } + void setBtnFocus() { mButton->setFocus(true); } void setEnabledColor( const LLColor4 &color ) { mTextEnabledColor = color; } void setDisabledColor( const LLColor4 &color ) { mTextDisabledColor = color; } @@ -121,7 +121,7 @@ public: virtual void setControlName(const std::string& control_name, LLView* context); - virtual BOOL isDirty() const; // Returns TRUE if the user has modified this control. + virtual bool isDirty() const; // Returns true if the user has modified this control. virtual void resetDirty(); // Clear dirty state protected: @@ -151,7 +151,7 @@ protected: // Build time optimization, generate once in .cpp file #ifndef LLCHECKBOXCTRL_CPP extern template class LLCheckBoxCtrl* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif #endif // LL_LLCHECKBOXCTRL_H diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 81031508ec..07d1be3762 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -171,13 +171,13 @@ void LLComboBox::initFromParams(const LLComboBox::Params& p) } // virtual -BOOL LLComboBox::postBuild() +bool LLComboBox::postBuild() { if (mControlVariable) { setValue(mControlVariable->getValue()); // selects the appropriate item } - return TRUE; + return true; } @@ -210,16 +210,16 @@ void LLComboBox::onCommit() // we have selected an existing item, blitz the manual text entry with // the properly capitalized item mTextEntry->setValue(getSimple()); - mTextEntry->setTentative(FALSE); + mTextEntry->setTentative(false); } setControlValue(getValue()); LLUICtrl::onCommit(); } // virtual -BOOL LLComboBox::isDirty() const +bool LLComboBox::isDirty() const { - BOOL grubby = FALSE; + bool grubby = false; if ( mList ) { grubby = mList->isDirty(); @@ -242,7 +242,7 @@ bool LLComboBox::itemExists(const std::string& name) } // add item "name" to menu -LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, BOOL enabled) +LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, bool enabled) { LLScrollListItem* item = mList->addSimpleElement(name, pos); item->setEnabled(enabled); @@ -261,7 +261,7 @@ LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, BOO } // add item "name" with a unique id to menu -LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, bool enabled ) { LLScrollListItem* item = mList->addSimpleElement(name, pos, id); item->setEnabled(enabled); @@ -280,7 +280,7 @@ LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAd } // add item "name" with attached userdata -LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, bool enabled ) { LLScrollListItem* item = mList->addSimpleElement(name, pos); item->setEnabled(enabled); @@ -300,7 +300,7 @@ LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddP } // add item "name" with attached generic data -LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, bool enabled ) { LLScrollListItem* item = mList->addSimpleElement(name, pos, value); item->setEnabled(enabled); @@ -323,17 +323,17 @@ LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos) return mList->addSeparator(pos); } -void LLComboBox::sortByName(BOOL ascending) +void LLComboBox::sortByName(bool ascending) { mList->sortOnce(0, ascending); } // Choose an item with a given name in the menu. -// Returns TRUE if the item was found. -BOOL LLComboBox::setSimple(const LLStringExplicit& name) +// Returns true if the item was found. +bool LLComboBox::setSimple(const LLStringExplicit& name) { - BOOL found = mList->selectItemByLabel(name, FALSE); + bool found = mList->selectItemByLabel(name, false); if (found) { @@ -347,7 +347,7 @@ BOOL LLComboBox::setSimple(const LLStringExplicit& name) // virtual void LLComboBox::setValue(const LLSD& value) { - BOOL found = mList->selectByValue(value); + bool found = mList->selectByValue(value); if (found) { LLScrollListItem* item = mList->getFirstSelected(); @@ -404,9 +404,9 @@ void LLComboBox::setLabel(const LLStringExplicit& name) if ( mTextEntry ) { mTextEntry->setText(name); - if (mList->selectItemByLabel(name, FALSE)) + if (mList->selectItemByLabel(name, false)) { - mTextEntry->setTentative(FALSE); + mTextEntry->setTentative(false); mLastSelectedIndex = mList->getFirstSelectedIndex(); } else @@ -428,7 +428,7 @@ void LLComboBox::updateLabel() if (mTextEntry) { mTextEntry->setText(getSelectedItemLabel()); - mTextEntry->setTentative(FALSE); + mTextEntry->setTentative(false); } // If combo box doesn't allow text entry update @@ -439,9 +439,9 @@ void LLComboBox::updateLabel() } } -BOOL LLComboBox::remove(const std::string& name) +bool LLComboBox::remove(const std::string& name) { - BOOL found = mList->selectItemByLabel(name); + bool found = mList->selectItemByLabel(name); if (found) { @@ -456,15 +456,15 @@ BOOL LLComboBox::remove(const std::string& name) return found; } -BOOL LLComboBox::remove(S32 index) +bool LLComboBox::remove(S32 index) { if (index < mList->getItemCount()) { mList->deleteSingleItem(index); setLabel(getSelectedItemLabel()); - return TRUE; + return true; } - return FALSE; + return false; } // Keyboard focus lost. @@ -480,7 +480,7 @@ void LLComboBox::onFocusLost() LLUICtrl::onFocusLost(); } -void LLComboBox::setButtonVisible(BOOL visible) +void LLComboBox::setButtonVisible(bool visible) { static LLUICachedControl drop_shadow_button ("DropShadowButton", 0); @@ -494,13 +494,13 @@ void LLComboBox::setButtonVisible(BOOL visible) text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button; } //mTextEntry->setRect(text_entry_rect); - mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); + mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true); } } -BOOL LLComboBox::setCurrentByIndex( S32 index ) +bool LLComboBox::setCurrentByIndex( S32 index ) { - BOOL found = mList->selectNthItem( index ); + bool found = mList->selectNthItem( index ); if (found) { setLabel(getSelectedItemLabel()); @@ -519,7 +519,7 @@ S32 LLComboBox::getCurrentIndex() const return -1; } -void LLComboBox::setEnabledByValue(const LLSD& value, BOOL enabled) +void LLComboBox::setEnabledByValue(const LLSD& value, bool enabled) { LLScrollListItem *found = mList->getItem(value); if (found) @@ -538,7 +538,7 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p) S32 shadow_size = drop_shadow_button; mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size, rect.mTop, rect.mRight, rect.mBottom)); - mButton->setTabStop(FALSE); + mButton->setTabStop(false); mButton->setHAlign(LLFontGL::HCENTER); LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); @@ -556,7 +556,7 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p) params.label(mLabel); mTextEntry = LLUICtrlFactory::create (params); mTextEntry->setText(cur_label); - mTextEntry->setIgnoreTab(TRUE); + mTextEntry->setIgnoreTab(true); addChild(mTextEntry); // clear label on button @@ -571,7 +571,7 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p) if (mTextEntry) { - mTextEntry->setVisible(FALSE); + mTextEntry->setVisible(false); } } } @@ -675,15 +675,15 @@ void LLComboBox::showList() // NB: this call will trigger the focuslost callback which will hide the list, so do it first // before finally showing the list - mList->setFocus(TRUE); + mList->setFocus(true); // Show the list and push the button down - mButton->setToggleState(TRUE); - mList->setVisible(TRUE); + mButton->setToggleState(true); + mList->setVisible(true); LLUI::getInstance()->addPopup(this); - setUseBoundingRect(TRUE); + setUseBoundingRect(true); // updateBoundingRect(); } @@ -702,11 +702,11 @@ void LLComboBox::hideList() else if(mLastSelectedIndex >= 0) mList->selectNthItem(mLastSelectedIndex); - mButton->setToggleState(FALSE); - mList->setVisible(FALSE); + mButton->setToggleState(false); + mList->setVisible(false); mList->mouseOverHighlightNthItem(-1); - setUseBoundingRect(FALSE); + setUseBoundingRect(false); LLUI::getInstance()->removePopup(this); // updateBoundingRect(); } @@ -732,7 +732,7 @@ void LLComboBox::onButtonMouseDown() showList(); } - setFocus( TRUE ); + setFocus( true ); // pass mouse capture on to list if button is depressed if (mButton->hasMouseCapture()) @@ -806,16 +806,16 @@ bool LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) return true; } -BOOL LLComboBox::handleKeyHere(KEY key, MASK mask) +bool LLComboBox::handleKeyHere(KEY key, MASK mask) { - BOOL result = FALSE; + bool result = false; if (hasFocus()) { if (mList->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) { hideList(); - return TRUE; + return true; } //give list a chance to pop up and handle key LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); @@ -838,7 +838,7 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask) // don't show list and don't eat key input when committing // free-form text entry with RETURN since user already knows // what they are trying to select - return FALSE; + return false; } // if selection has changed, pop open list else if (mList->getLastSelectedItem() != last_selected_item @@ -881,12 +881,12 @@ void LLComboBox::setTextEntry(const LLStringExplicit& text) if (mTextEntry) { mTextEntry->setText(text); - mHasAutocompletedText = FALSE; + mHasAutocompletedText = false; updateSelection(); } } -void LLComboBox::setKeystrokeOnEsc(BOOL enable) +void LLComboBox::setKeystrokeOnEsc(bool enable) { if (mTextEntry) { @@ -905,9 +905,9 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) if (key == KEY_BACKSPACE || key == KEY_DELETE) { - if (mList->selectItemByLabel(line_editor->getText(), FALSE)) + if (mList->selectItemByLabel(line_editor->getText(), false)) { - line_editor->setTentative(FALSE); + line_editor->setTentative(false); mLastSelectedIndex = mList->getFirstSelectedIndex(); } else @@ -942,7 +942,7 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) } } line_editor->selectAll(); - line_editor->setTentative(FALSE); + line_editor->setTentative(false); } else if (key == KEY_UP) { @@ -957,7 +957,7 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) } } line_editor->selectAll(); - line_editor->setTentative(FALSE); + line_editor->setTentative(false); } else { @@ -986,20 +986,20 @@ void LLComboBox::updateSelection() prearrangeList(mTextEntry->getText()); } - if (mList->selectItemByLabel(full_string, FALSE)) + if (mList->selectItemByLabel(full_string, false)) { - mTextEntry->setTentative(FALSE); + mTextEntry->setTentative(false); mLastSelectedIndex = mList->getFirstSelectedIndex(); } - else if (mList->selectItemByPrefix(left_wstring, FALSE)) + else if (mList->selectItemByPrefix(left_wstring, false)) { LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel()); LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size()); mTextEntry->setText(wstring_to_utf8str(wtext)); mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size()); mTextEntry->endSelection(); - mTextEntry->setTentative(FALSE); - mHasAutocompletedText = TRUE; + mTextEntry->setTentative(false); + mHasAutocompletedText = true; mLastSelectedIndex = mList->getFirstSelectedIndex(); } else // no matching items found @@ -1007,7 +1007,7 @@ void LLComboBox::updateSelection() mList->deselectAllItems(); mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion mTextEntry->setTentative(mTextEntryTentative); - mHasAutocompletedText = FALSE; + mHasAutocompletedText = false; mLastSelectedIndex = -1; } } @@ -1020,7 +1020,7 @@ void LLComboBox::onTextCommit(const LLSD& data) mTextEntry->selectAll(); } -void LLComboBox::setFocus(BOOL b) +void LLComboBox::setFocus(bool b) { LLUICtrl::setFocus(b); @@ -1029,7 +1029,7 @@ void LLComboBox::setFocus(BOOL b) mList->clearSearchString(); if (mList->getVisible()) { - mList->setFocus(TRUE); + mList->setFocus(true); } } } @@ -1097,7 +1097,7 @@ void LLComboBox::imageLoaded() { LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * drop_shadow_button; - mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); + mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true); } } } @@ -1142,7 +1142,7 @@ void LLComboBox::clearRows() mList->clearRows(); } -void LLComboBox::sortByColumn(const std::string& name, BOOL ascending) +void LLComboBox::sortByColumn(const std::string& name, bool ascending) { mList->sortByColumn(name, ascending); } @@ -1150,9 +1150,9 @@ void LLComboBox::sortByColumn(const std::string& name, BOOL ascending) //============================================================================ //LLCtrlSelectionInterface functions -BOOL LLComboBox::setCurrentByID(const LLUUID& id) +bool LLComboBox::setCurrentByID(const LLUUID& id) { - BOOL found = mList->selectByID( id ); + bool found = mList->selectByID( id ); if (found) { @@ -1167,9 +1167,9 @@ LLUUID LLComboBox::getCurrentID() const { return mList->getStringUUIDSelectedItem(); } -BOOL LLComboBox::setSelectedByValue(const LLSD& value, BOOL selected) +bool LLComboBox::setSelectedByValue(const LLSD& value, bool selected) { - BOOL found = mList->setSelectedByValue(value, selected); + bool found = mList->setSelectedByValue(value, selected); if (found) { setLabel(getSelectedItemLabel()); @@ -1182,32 +1182,32 @@ LLSD LLComboBox::getSelectedValue() return mList->getSelectedValue(); } -BOOL LLComboBox::isSelected(const LLSD& value) const +bool LLComboBox::isSelected(const LLSD& value) const { return mList->isSelected(value); } -BOOL LLComboBox::operateOnSelection(EOperation op) +bool LLComboBox::operateOnSelection(EOperation op) { if (op == OP_DELETE) { mList->deleteSelectedItems(); - return TRUE; + return true; } - return FALSE; + return false; } -BOOL LLComboBox::operateOnAll(EOperation op) +bool LLComboBox::operateOnAll(EOperation op) { if (op == OP_DELETE) { clearRows(); - return TRUE; + return true; } - return FALSE; + return false; } -BOOL LLComboBox::selectItemRange( S32 first, S32 last ) +bool LLComboBox::selectItemRange( S32 first, S32 last ) { return mList->selectItemRange(first, last); } diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index d87ce9189e..97ac6653d5 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -94,7 +94,7 @@ public: virtual ~LLComboBox(); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); protected: friend class LLUICtrlFactory; @@ -112,17 +112,17 @@ public: virtual void onFocusLost(); virtual bool handleToolTip(S32 x, S32 y, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); virtual bool handleUnicodeCharHere(llwchar uni_char); // LLUICtrl interface virtual void clear(); // select nothing virtual void onCommit(); - virtual BOOL acceptsTextInput() const { return mAllowTextEntry; } - virtual BOOL isDirty() const; // Returns TRUE if the user has modified this control. + virtual bool acceptsTextInput() const { return mAllowTextEntry; } + virtual bool isDirty() const; // Returns true if the user has modified this control. virtual void resetDirty(); // Clear dirty state - virtual void setFocus(BOOL b); + virtual void setFocus(bool b); // Selects item by underlying LLSD value, using LLSD::asString() matching. // For simple items, this is just the name of the label. @@ -133,21 +133,21 @@ public: virtual LLSD getValue() const; void setTextEntry(const LLStringExplicit& text); - void setKeystrokeOnEsc(BOOL enable); + void setKeystrokeOnEsc(bool enable); - LLScrollListItem* add(const std::string& name, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); // add item "name" to menu - LLScrollListItem* add(const std::string& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); - LLScrollListItem* add(const std::string& name, void* userdata, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); - LLScrollListItem* add(const std::string& name, LLSD value, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* add(const std::string& name, EAddPosition pos = ADD_BOTTOM, bool enabled = true); // add item "name" to menu + LLScrollListItem* add(const std::string& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true); + LLScrollListItem* add(const std::string& name, void* userdata, EAddPosition pos = ADD_BOTTOM, bool enabled = true); + LLScrollListItem* add(const std::string& name, LLSD value, EAddPosition pos = ADD_BOTTOM, bool enabled = true); LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM); - BOOL remove( S32 index ); // remove item by index, return TRUE if found and removed + bool remove( S32 index ); // remove item by index, return true if found and removed void removeall() { clearRows(); } bool itemExists(const std::string& name); - void sortByName(BOOL ascending = TRUE); // Sort the entries in the combobox by name + void sortByName(bool ascending = true); // Sort the entries in the combobox by name - // Select current item by name using selectItemByLabel. Returns FALSE if not found. - BOOL setSimple(const LLStringExplicit& name); + // Select current item by name using selectItemByLabel. Returns false if not found. + bool setSimple(const LLStringExplicit& name); // Get name of current item. Returns an empty string if not found. const std::string getSimple() const; // Get contents of column x of selected row @@ -160,12 +160,12 @@ public: // Updates the combobox label to match the selected list item. void updateLabel(); - BOOL remove(const std::string& name); // remove item "name", return TRUE if found and removed + bool remove(const std::string& name); // remove item "name", return true if found and removed - BOOL setCurrentByIndex( S32 index ); + bool setCurrentByIndex( S32 index ); S32 getCurrentIndex() const; - void setEnabledByValue(const LLSD& value, BOOL enabled); + void setEnabledByValue(const LLSD& value, bool enabled); void createLineEditor(const Params&); @@ -183,21 +183,21 @@ public: virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD()); virtual void clearRows(); - virtual void sortByColumn(const std::string& name, BOOL ascending); + virtual void sortByColumn(const std::string& name, bool ascending); // LLCtrlSelectionInterface functions - virtual BOOL getCanSelect() const { return TRUE; } - virtual BOOL selectFirstItem() { return setCurrentByIndex(0); } - virtual BOOL selectNthItem( S32 index ) { return setCurrentByIndex(index); } - virtual BOOL selectItemRange( S32 first, S32 last ); + virtual bool getCanSelect() const { return true; } + virtual bool selectFirstItem() { return setCurrentByIndex(0); } + virtual bool selectNthItem( S32 index ) { return setCurrentByIndex(index); } + virtual bool selectItemRange( S32 first, S32 last ); virtual S32 getFirstSelectedIndex() const { return getCurrentIndex(); } - virtual BOOL setCurrentByID( const LLUUID& id ); + virtual bool setCurrentByID( const LLUUID& id ); virtual LLUUID getCurrentID() const; // LLUUID::null if no items in menu - virtual BOOL setSelectedByValue(const LLSD& value, BOOL selected); + virtual bool setSelectedByValue(const LLSD& value, bool selected); virtual LLSD getSelectedValue(); - virtual BOOL isSelected(const LLSD& value) const; - virtual BOOL operateOnSelection(EOperation op); - virtual BOOL operateOnAll(EOperation op); + virtual bool isSelected(const LLSD& value) const; + virtual bool operateOnSelection(EOperation op); + virtual bool operateOnAll(EOperation op); //======================================================================== @@ -214,7 +214,7 @@ public: */ boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); } - void setButtonVisible(BOOL visible); + void setButtonVisible(bool visible); void onButtonMouseDown(); void onListMouseUp(); @@ -234,13 +234,13 @@ protected: EPreferredPosition mListPosition; LLPointer mArrowImage; LLUIString mLabel; - BOOL mHasAutocompletedText; + bool mHasAutocompletedText; private: - BOOL mAllowTextEntry; - BOOL mAllowNewValues; + bool mAllowTextEntry; + bool mAllowNewValues; S32 mMaxChars; - BOOL mTextEntryTentative; + bool mTextEntryTentative; commit_callback_t mPrearrangeCallback; commit_callback_t mTextEntryCallback; commit_callback_t mTextChangedCallback; diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 8fc2978bdd..c9d5f0bf80 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -77,7 +77,7 @@ void LLConsole::setLinePersistTime(F32 seconds) mFadeTime = mLinePersistTime - FADE_DURATION; } -void LLConsole::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLConsole::reshape(S32 width, S32 height, bool called_from_parent) { S32 new_width = llmax(50, llmin(getRect().getWidth(), width)); S32 new_height = llmax(llfloor(mFont->getLineHeight()) + 15, llmin(getRect().getHeight(), height)); diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h index 04f5e71609..2b144f03de 100644 --- a/indra/llui/llconsole.h +++ b/indra/llui/llconsole.h @@ -132,7 +132,7 @@ public: // each line lasts this long after being added void setLinePersistTime(F32 seconds); - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + void reshape(S32 width, S32 height, bool called_from_parent = true); // -1 = monospace, 0 means small, font size = 1 means big void setFontSize(S32 size_index); diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp index 415cdced4d..c2fee0871c 100644 --- a/indra/llui/llcontainerview.cpp +++ b/indra/llui/llcontainerview.cpp @@ -57,11 +57,11 @@ LLContainerView::~LLContainerView() // Children all cleaned up by default view destructor. } -BOOL LLContainerView::postBuild() +bool LLContainerView::postBuild() { setDisplayChildren(mDisplayChildren); - reshape(getRect().getWidth(), getRect().getHeight(), FALSE); - return TRUE; + reshape(getRect().getWidth(), getRect().getHeight(), false); + return true; } bool LLContainerView::addChild(LLView* child, S32 tab_group) @@ -91,7 +91,7 @@ bool LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask) if( mShowLabel && (y >= getRect().getHeight() - 10) ) { setDisplayChildren(!mDisplayChildren); - reshape(getRect().getWidth(), getRect().getHeight(), FALSE); + reshape(getRect().getWidth(), getRect().getHeight(), false); handled = true; } } @@ -128,7 +128,7 @@ void LLContainerView::draw() } -void LLContainerView::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLContainerView::reshape(S32 width, S32 height, bool called_from_parent) { LLRect scroller_rect; scroller_rect.setOriginAndSize(0, 0, width, height); @@ -159,7 +159,7 @@ void LLContainerView::reshape(S32 width, S32 height, BOOL called_from_parent) } } -void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent) +void LLContainerView::arrange(S32 width, S32 height, bool called_from_parent) { // Determine the sizes and locations of all contained views S32 total_height = 0; @@ -242,7 +242,7 @@ void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent) { if (getParent()) { - getParent()->reshape(getParent()->getRect().getWidth(), getParent()->getRect().getHeight(), FALSE); + getParent()->reshape(getParent()->getRect().getWidth(), getParent()->getRect().getHeight(), false); } } @@ -288,7 +288,7 @@ void LLContainerView::setLabel(const std::string& label) mLabel = label; } -void LLContainerView::setDisplayChildren(const BOOL displayChildren) +void LLContainerView::setDisplayChildren(const bool displayChildren) { mDisplayChildren = displayChildren; for (child_list_const_iter_t child_iter = getChildList()->begin(); diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h index f439689ceb..82e7384676 100644 --- a/indra/llui/llcontainerview.h +++ b/indra/llui/llcontainerview.h @@ -49,8 +49,8 @@ public: Optional display_children; Params() : label("label"), - show_label("show_label", FALSE), - display_children("display_children", TRUE) + show_label("show_label", false), + display_children("display_children", true) { changeDefault(mouse_opaque, false); } @@ -65,7 +65,7 @@ protected: public: ~LLContainerView(); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); @@ -73,22 +73,22 @@ public: /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); /*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options. void setLabel(const std::string& label); - void showLabel(BOOL show) { mShowLabel = show; } - void setDisplayChildren(const BOOL displayChildren); - BOOL getDisplayChildren() { return mDisplayChildren; } + void showLabel(bool show) { mShowLabel = show; } + void setDisplayChildren(const bool displayChildren); + bool getDisplayChildren() { return mDisplayChildren; } void setScrollContainer(LLScrollContainer* scroll) {mScrollContainer = scroll;} private: LLScrollContainer* mScrollContainer; - void arrange(S32 width, S32 height, BOOL called_from_parent = TRUE); - BOOL mShowLabel; + void arrange(S32 width, S32 height, bool called_from_parent = true); + bool mShowLabel; protected: - BOOL mDisplayChildren; + bool mDisplayChildren; std::string mLabel; }; #endif // LL_CONTAINERVIEW_ diff --git a/indra/llui/llctrlselectioninterface.cpp b/indra/llui/llctrlselectioninterface.cpp index 7e886aff48..87d52da187 100644 --- a/indra/llui/llctrlselectioninterface.cpp +++ b/indra/llui/llctrlselectioninterface.cpp @@ -33,14 +33,14 @@ LLCtrlSelectionInterface::~LLCtrlSelectionInterface() { } -BOOL LLCtrlSelectionInterface::selectByValue(LLSD value) +bool LLCtrlSelectionInterface::selectByValue(LLSD value) { - return setSelectedByValue(value, TRUE); + return setSelectedByValue(value, true); } -BOOL LLCtrlSelectionInterface::deselectByValue(LLSD value) +bool LLCtrlSelectionInterface::deselectByValue(LLSD value) { - return setSelectedByValue(value, FALSE); + return setSelectedByValue(value, false); } diff --git a/indra/llui/llctrlselectioninterface.h b/indra/llui/llctrlselectioninterface.h index a7b089c8f9..0907b58b5c 100644 --- a/indra/llui/llctrlselectioninterface.h +++ b/indra/llui/llctrlselectioninterface.h @@ -47,29 +47,29 @@ public: OP_DESELECT, }; - virtual BOOL getCanSelect() const = 0; + virtual bool getCanSelect() const = 0; virtual S32 getItemCount() const = 0; - virtual BOOL selectFirstItem() = 0; - virtual BOOL selectNthItem( S32 index ) = 0; - virtual BOOL selectItemRange( S32 first, S32 last ) = 0; + virtual bool selectFirstItem() = 0; + virtual bool selectNthItem( S32 index ) = 0; + virtual bool selectItemRange( S32 first, S32 last ) = 0; virtual S32 getFirstSelectedIndex() const = 0; // TomY TODO: Simply cast the UUIDs to LLSDs, using the selectByValue function - virtual BOOL setCurrentByID( const LLUUID& id ) = 0; + virtual bool setCurrentByID( const LLUUID& id ) = 0; virtual LLUUID getCurrentID() const = 0; - BOOL selectByValue(const LLSD value); - BOOL deselectByValue(const LLSD value); - virtual BOOL setSelectedByValue(const LLSD& value, BOOL selected) = 0; + bool selectByValue(const LLSD value); + bool deselectByValue(const LLSD value); + virtual bool setSelectedByValue(const LLSD& value, bool selected) = 0; virtual LLSD getSelectedValue() = 0; - virtual BOOL isSelected(const LLSD& value) const = 0; + virtual bool isSelected(const LLSD& value) const = 0; - virtual BOOL operateOnSelection(EOperation op) = 0; - virtual BOOL operateOnAll(EOperation op) = 0; + virtual bool operateOnSelection(EOperation op) = 0; + virtual bool operateOnAll(EOperation op) = 0; }; class LLCtrlListInterface : public LLCtrlSelectionInterface @@ -88,7 +88,7 @@ public: virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) = 0; virtual void clearRows() = 0; - virtual void sortByColumn(const std::string& name, BOOL ascending) = 0; + virtual void sortByColumn(const std::string& name, bool ascending) = 0; }; class LLCtrlScrollInterface diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp index c937d190c6..4495986d81 100644 --- a/indra/llui/lldockablefloater.cpp +++ b/indra/llui/lldockablefloater.cpp @@ -40,9 +40,9 @@ void LLDockableFloater::init(LLDockableFloater* thiz) thiz->resetInstance(); // all dockable floaters should have close, dock and minimize buttons - thiz->setCanClose(TRUE); + thiz->setCanClose(true); thiz->setCanDock(true); - thiz->setCanMinimize(TRUE); + thiz->setCanMinimize(true); thiz->setOverlapsScreenChannel(false); thiz->mForceDocking = false; } @@ -74,7 +74,7 @@ LLDockableFloater::~LLDockableFloater() { } -BOOL LLDockableFloater::postBuild() +bool LLDockableFloater::postBuild() { // Remember we should force docking when the floater is opened for the first time if (mIsDockedStateForcedCallback != NULL && mIsDockedStateForcedCallback()) @@ -108,14 +108,14 @@ void LLDockableFloater::toggleInstance(const LLSD& sdname) // if floater undocked else if (instance != NULL) { - instance->setMinimized(FALSE); + instance->setMinimized(false); if (instance->getVisible()) { - instance->setVisible(FALSE); + instance->setVisible(false); } else { - instance->setVisible(TRUE); + instance->setVisible(true); gFloaterView->bringToFront(instance); } } @@ -127,13 +127,13 @@ void LLDockableFloater::resetInstance() { if (sInstanceHandle.get() != NULL && sInstanceHandle.get()->isDocked()) { - sInstanceHandle.get()->setVisible(FALSE); + sInstanceHandle.get()->setVisible(false); } sInstanceHandle = getHandle(); } } -void LLDockableFloater::setVisible(BOOL visible) +void LLDockableFloater::setVisible(bool visible) { // Force docking if requested if (visible && mForceDocking) @@ -160,12 +160,12 @@ void LLDockableFloater::setVisible(BOOL visible) LLFloater::setVisible(visible); } -void LLDockableFloater::setMinimized(BOOL minimize) +void LLDockableFloater::setMinimized(bool minimize) { if(minimize && isDocked()) { // minimizing a docked floater just hides it - setVisible(FALSE); + setVisible(false); } else { @@ -185,14 +185,14 @@ LLView * LLDockableFloater::getDockWidget() void LLDockableFloater::onDockHidden() { - setCanDock(FALSE); + setCanDock(false); } void LLDockableFloater::onDockShown() { if (!isMinimized()) { - setCanDock(TRUE); + setCanDock(true); } } diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h index 5d90b3ef4e..03b8be39a6 100644 --- a/indra/llui/lldockablefloater.h +++ b/indra/llui/lldockablefloater.h @@ -81,7 +81,7 @@ public: * If descendant class overrides postBuild() in order to perform specific * construction then it must still invoke its superclass' implementation. */ - /* virtula */BOOL postBuild(); + /* virtula */bool postBuild(); /* virtual */void setDocked(bool docked, bool pop_on_undock = true); /* virtual */void draw(); @@ -89,13 +89,13 @@ public: * If descendant class overrides setVisible() then it must still invoke its * superclass' implementation. */ - /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setVisible(bool visible); /** * If descendant class overrides setMinimized() then it must still invoke its * superclass' implementation. */ - /*virtual*/ void setMinimized(BOOL minimize); + /*virtual*/ void setMinimized(bool minimize); LLView * getDockWidget(); @@ -129,7 +129,7 @@ protected: // Checks if docking should be forced. // It may be useful e.g. if floater created in mouselook mode (see EXT-5609) - boost::function mIsDockedStateForcedCallback; + boost::function mIsDockedStateForcedCallback; private: std::unique_ptr mDockControl; diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index b2e342411c..d3b4dd2bd3 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -57,7 +57,7 @@ LLDragHandle::LLDragHandle(const LLDragHandle::Params& p) mLastMouseScreenY( 0 ), mTitleBox( NULL ), mMaxTitleWidth( 0 ), - mForeground( TRUE ), + mForeground( true ), mDragHighlightColor(p.drag_highlight_color()), mDragShadowColor(p.drag_shadow_color()) @@ -79,7 +79,7 @@ void LLDragHandle::initFromParams(const LLDragHandle::Params& p) setTitle( p.label ); } -void LLDragHandle::setTitleVisible(BOOL visible) +void LLDragHandle::setTitleVisible(bool visible) { if(mTitleBox) { @@ -160,7 +160,7 @@ void LLDragHandleTop::draw() LLRect title_rect = mTitleBox->getRect(); S32 title_right = title_rect.mLeft + mTitleWidth; - BOOL show_right_side = title_right < getRect().getWidth(); + bool show_right_side = title_right < getRect().getWidth(); for( S32 i=0; i<4; i++ ) { @@ -211,7 +211,7 @@ void LLDragHandleLeft::draw() // no titles yet //LLRect title_rect = mTitleBox->getRect(); //S32 title_right = title_rect.mLeft + mTitleWidth; - //BOOL show_right_side = title_right < getRect().getWidth(); + //bool show_right_side = title_right < getRect().getWidth(); S32 line = left; for( S32 i=0; i<4; i++ ) @@ -256,13 +256,13 @@ void LLDragHandleTop::reshapeTitleBox() mTitleBox->setShape( title_rect ); } -void LLDragHandleTop::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLDragHandleTop::reshape(S32 width, S32 height, bool called_from_parent) { LLView::reshape(width, height, called_from_parent); reshapeTitleBox(); } -void LLDragHandleLeft::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLDragHandleLeft::reshape(S32 width, S32 height, bool called_from_parent) { LLView::reshape(width, height, called_from_parent); } diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h index 7f6ae47201..bb5ee43a70 100644 --- a/indra/llui/lldraghandle.h +++ b/indra/llui/lldraghandle.h @@ -61,13 +61,13 @@ public: virtual void setValue(const LLSD& value); - void setForeground(BOOL b) { mForeground = b; } - BOOL getForeground() const { return mForeground; } + void setForeground(bool b) { mForeground = b; } + bool getForeground() const { return mForeground; } void setMaxTitleWidth(S32 max_width) {mMaxTitleWidth = llmin(max_width, mMaxTitleWidth); } S32 getMaxTitleWidth() const { return mMaxTitleWidth; } void setButtonsRect(const LLRect& rect){ mButtonsRect = rect; } LLRect getButtonsRect() { return mButtonsRect; } - void setTitleVisible(BOOL visible); + void setTitleVisible(bool visible); virtual void setTitle( const std::string& title ) = 0; virtual std::string getTitle() const = 0; @@ -93,7 +93,7 @@ private: LLUIColor mDragHighlightColor; LLUIColor mDragShadowColor; S32 mMaxTitleWidth; - BOOL mForeground; + bool mForeground; // Pixels near the edge to snap floaters. static S32 sSnapMargin; @@ -111,7 +111,7 @@ public: virtual void setTitle( const std::string& title ); virtual std::string getTitle() const; virtual void draw(); - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); private: void reshapeTitleBox(); @@ -129,7 +129,7 @@ public: virtual void setTitle( const std::string& title ); virtual std::string getTitle() const; virtual void draw(); - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); }; diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h index cd4fea8c52..32e9aac20b 100644 --- a/indra/llui/lleditmenuhandler.h +++ b/indra/llui/lleditmenuhandler.h @@ -35,29 +35,29 @@ public: virtual ~LLEditMenuHandler(); virtual void undo() {}; - virtual BOOL canUndo() const { return FALSE; } + virtual bool canUndo() const { return false; } virtual void redo() {}; - virtual BOOL canRedo() const { return FALSE; } + virtual bool canRedo() const { return false; } virtual void cut() {}; - virtual BOOL canCut() const { return FALSE; } + virtual bool canCut() const { return false; } virtual void copy() {}; - virtual BOOL canCopy() const { return FALSE; } + virtual bool canCopy() const { return false; } virtual void paste() {}; - virtual BOOL canPaste() const { return FALSE; } + virtual bool canPaste() const { return false; } // "delete" is a keyword virtual void doDelete() {}; - virtual BOOL canDoDelete() const { return FALSE; } + virtual bool canDoDelete() const { return false; } virtual void selectAll() {}; - virtual BOOL canSelectAll() const { return FALSE; } + virtual bool canSelectAll() const { return false; } virtual void deselect() {}; - virtual BOOL canDeselect() const { return FALSE; } + virtual bool canDeselect() const { return false; } // TODO: Instead of being a public data member, it would be better to hide it altogether // and have a "set" method and then a bunch of static versions of the cut, copy, paste diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp index d62874d793..6488bd3941 100644 --- a/indra/llui/llfiltereditor.cpp +++ b/indra/llui/llfiltereditor.cpp @@ -33,7 +33,7 @@ LLFilterEditor::LLFilterEditor(const LLFilterEditor::Params& p) : LLSearchEditor(p) { - setCommitOnFocusLost(FALSE); // we'll commit on every keystroke, don't re-commit when we take focus away (i.e. we go to interact with the actual results!) + setCommitOnFocusLost(false); // we'll commit on every keystroke, don't re-commit when we take focus away (i.e. we go to interact with the actual results!) } diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 460bd0945b..85fda7928c 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -48,7 +48,7 @@ LLFlatListView::Params::Params() no_items_text("no_items_text") {}; -void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) +void LLFlatListView::reshape(S32 width, S32 height, bool called_from_parent /* = true */) { S32 delta = height - getRect().getHeight(); LLScrollContainer::reshape(width, height, called_from_parent); @@ -528,7 +528,7 @@ void LLFlatListView::draw() } // virtual -BOOL LLFlatListView::postBuild() +bool LLFlatListView::postBuild() { setTabStop(true); return LLScrollContainer::postBuild(); @@ -610,7 +610,7 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) return; } - setFocus(TRUE); + setFocus(true); bool select_item = !isSelected(item_pair); @@ -714,10 +714,10 @@ void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask) onItemMouseClick(item_pair, mask); } -BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask) +bool LLFlatListView::handleKeyHere(KEY key, MASK mask) { - BOOL reset_selection = (mask != MASK_SHIFT); - BOOL handled = FALSE; + bool reset_selection = (mask != MASK_SHIFT); + bool handled = false; switch (key) { case KEY_RETURN: @@ -725,7 +725,7 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask) if (mSelectedItemPairs.size() && mask == MASK_NONE) { mOnReturnSignal(this, getValue()); - handled = TRUE; + handled = true; } break; } @@ -753,7 +753,7 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask) { if (mask == MASK_NONE) { - setFocus(FALSE); // pass focus to the game area (EXT-8357) + setFocus(false); // pass focus to the game area (EXT-8357) } break; } @@ -779,7 +779,7 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask) localRectToScreen(selected_rc, &screen_rc); notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/ - handled = TRUE; + handled = true; } return handled ? handled : LLScrollContainer::handleKeyHere(key, mask); @@ -1040,7 +1040,7 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti return false; } -BOOL LLFlatListView::canSelectAll() const +bool LLFlatListView::canSelectAll() const { return 0 != size() && mAllowSelection && mMultipleSelection; } @@ -1198,14 +1198,14 @@ void LLFlatListView::onFocusReceived() { if (size()) { - mSelectedItemsBorder->setVisible(TRUE); + mSelectedItemsBorder->setVisible(true); } gEditMenuHandler = this; } // virtual void LLFlatListView::onFocusLost() { - mSelectedItemsBorder->setVisible(FALSE); + mSelectedItemsBorder->setVisible(false); // Route menu back to the default if (gEditMenuHandler == this) { diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index d47c1cf333..a19413efa0 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -113,7 +113,7 @@ public: }; // disable traversal when finding widget to hand focus off to - /*virtual*/ BOOL canFocusChildren() const { return FALSE; } + /*virtual*/ bool canFocusChildren() const { return false; } /** * Connects callback to signal called when Return key is pressed. @@ -121,7 +121,7 @@ public: boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); } /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */ - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); /** Returns full rect of child panel */ const LLRect& getItemsRect() const; @@ -345,7 +345,7 @@ protected: virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection); - virtual BOOL canSelectAll() const; + virtual bool canSelectAll() const; virtual void selectAll(); virtual bool isSelected(item_pair_t* item_pair) const; @@ -363,9 +363,9 @@ protected: */ void notifyParentItemsRectChanged(); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); - virtual BOOL postBuild(); + virtual bool postBuild(); virtual void onFocusReceived(); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index e90dab1a99..3a6ee50a68 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -117,7 +117,7 @@ LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] = }; LLMultiFloater* LLFloater::sHostp = NULL; -BOOL LLFloater::sQuitting = FALSE; // Flag to prevent storing visibility controls while quitting +bool LLFloater::sQuitting = false; // Flag to prevent storing visibility controls while quitting LLFloaterView* gFloaterView = NULL; @@ -260,15 +260,15 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mHeaderHeight(p.header_height), mLegacyHeaderHeight(p.legacy_header_height), mDefaultRectForGroup(true), - mMinimized(FALSE), - mForeground(FALSE), - mFirstLook(TRUE), + mMinimized(false), + mForeground(false), + mFirstLook(true), mButtonScale(1.0f), - mAutoFocus(TRUE), // automatically take focus when opened + mAutoFocus(true), // automatically take focus when opened mCanDock(false), mDocked(false), mTornOff(false), - mHasBeenDraggedWhileMinimized(FALSE), + mHasBeenDraggedWhileMinimized(false), mPreviousMinimizedBottom(0), mPreviousMinimizedLeft(0), mDefaultRelativeX(p.rel_x), @@ -280,10 +280,10 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) // mNotificationContext = new LLFloaterNotificationContext(getHandle()); // Clicks stop here. - setMouseOpaque(TRUE); + setMouseOpaque(true); // Floaters always draw their background, unlike every other panel. - setBackgroundVisible(TRUE); + setBackgroundVisible(true); // Floaters start not minimized. When minimized, they save their // prior rectangle to be used on restore. @@ -306,28 +306,28 @@ void LLFloater::initFloater(const Params& p) // Close button. if (mCanClose) { - mButtonsEnabled[BUTTON_CLOSE] = TRUE; + mButtonsEnabled[BUTTON_CLOSE] = true; } // Help button: '?' //SL-14050 Disable all Help question marks - mButtonsEnabled[BUTTON_HELP] = FALSE; + mButtonsEnabled[BUTTON_HELP] = false; // Minimize button only for top draggers if ( !mDragOnLeft && mCanMinimize ) { - mButtonsEnabled[BUTTON_MINIMIZE] = TRUE; + mButtonsEnabled[BUTTON_MINIMIZE] = true; } if(mCanDock) { - mButtonsEnabled[BUTTON_DOCK] = TRUE; + mButtonsEnabled[BUTTON_DOCK] = true; } buildButtons(p); // Floaters are created in the invisible state - setVisible(FALSE); + setVisible(false); if (!getParent()) { @@ -529,7 +529,7 @@ LLFloater::~LLFloater() // This is important so that floaters with persistent rects (i.e., those // created with rect control rather than an LLRect) are restored in their // correct, non-minimized positions. - setMinimized( FALSE ); + setMinimized( false ); delete mDragHandle; for (S32 i = 0; i < 4; i++) @@ -597,12 +597,12 @@ LLControlGroup* LLFloater::getControlGroup() return LLUI::getInstance()->mSettingGroups["account"]; } -void LLFloater::setVisible( BOOL visible ) +void LLFloater::setVisible( bool visible ) { LLPanel::setVisible(visible); // calls onVisibilityChange() if( visible && mFirstLook ) { - mFirstLook = FALSE; + mFirstLook = false; } if( !visible ) @@ -631,7 +631,7 @@ void LLFloater::setVisible( BOOL visible ) } -void LLFloater::setIsSingleInstance(BOOL is_single_instance) +void LLFloater::setIsSingleInstance(bool is_single_instance) { mSingleInstance = is_single_instance; if (!mIsReuseInitialized) @@ -642,12 +642,12 @@ void LLFloater::setIsSingleInstance(BOOL is_single_instance) // virtual -void LLFloater::onVisibilityChange ( BOOL new_visibility ) +void LLFloater::onVisibilityChange ( bool new_visibility ) { if (new_visibility) { if (getHost()) - getHost()->setFloaterFlashing(this, FALSE); + getHost()->setFloaterFlashing(this, false); } LLPanel::onVisibilityChange ( new_visibility ); } @@ -680,7 +680,7 @@ void LLFloater::openFloater(const LLSD& key) if (getHost() != NULL) { - getHost()->setMinimized(FALSE); + getHost()->setMinimized(false); getHost()->setVisibleAndFrontmost(mAutoFocus); getHost()->showFloater(this); } @@ -692,7 +692,7 @@ void LLFloater::openFloater(const LLSD& key) floater_to_stack = LLFloaterReg::getLastFloaterCascading(); } applyControlsAndPosition(floater_to_stack); - setMinimized(FALSE); + setMinimized(false); setVisibleAndFrontmost(mAutoFocus); } @@ -713,7 +713,7 @@ void LLFloater::closeFloater(bool app_quitting) // Always unminimize before trying to close. // Most of the time the user will never see this state. - setMinimized(FALSE); + setMinimized(false); if (canClose()) { @@ -745,7 +745,7 @@ void LLFloater::closeFloater(bool app_quitting) LLFloater* dependee = mDependeeHandle.get(); if (dependee && !dependee->isDead()) { - dependee->setFocus(TRUE); + dependee->setFocus(true); } } } @@ -787,11 +787,11 @@ void LLFloater::closeFloater(bool app_quitting) // Hide the instance if (getHost()) { - getHost()->setVisible(FALSE); + getHost()->setVisible(false); } else { - setVisible(FALSE); + setVisible(false); if (!mReuseInstance) { destroy(); @@ -800,7 +800,7 @@ void LLFloater::closeFloater(bool app_quitting) } else { - setVisible(FALSE); // hide before destroying (so onVisibilityChange() gets called) + setVisible(false); // hide before destroying (so onVisibilityChange() gets called) if (!mReuseInstance) { destroy(); @@ -824,7 +824,7 @@ void LLFloater::closeHostedFloater() } /*virtual*/ -void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLFloater::reshape(S32 width, S32 height, bool called_from_parent) { LLPanel::reshape(width, height, called_from_parent); } @@ -833,7 +833,7 @@ void LLFloater::releaseFocus() { LLUI::getInstance()->removePopup(this); - setFocus(FALSE); + setFocus(false); if( gFocusMgr.childHasMouseCapture( this ) ) { @@ -1107,12 +1107,12 @@ std::string LLFloater::getShortTitle() const } } -BOOL LLFloater::canSnapTo(const LLView* other_view) +bool LLFloater::canSnapTo(const LLView* other_view) { if (NULL == other_view) { LL_WARNS() << "other_view is NULL" << LL_ENDL; - return FALSE; + return false; } if (other_view != getParent()) @@ -1123,7 +1123,7 @@ BOOL LLFloater::canSnapTo(const LLView* other_view) && mDependents.find(other_floaterp->getHandle()) != mDependents.end()) { // this is a dependent that is already snapped to us, so don't snap back to it - return FALSE; + return false; } } @@ -1213,16 +1213,16 @@ void LLFloater::handleReshape(const LLRect& new_rect, bool by_user) else { // If minimized, and origin has changed, set - // mHasBeenDraggedWhileMinimized to TRUE + // mHasBeenDraggedWhileMinimized to true if ((new_rect.mLeft != old_rect.mLeft) || (new_rect.mBottom != old_rect.mBottom)) { - mHasBeenDraggedWhileMinimized = TRUE; + mHasBeenDraggedWhileMinimized = true; } } } -void LLFloater::setMinimized(BOOL minimize) +void LLFloater::setMinimized(bool minimize) { const LLFloater::Params& default_params = LLFloater::getDefaultParams(); S32 floater_header_size = default_params.header_height; @@ -1238,7 +1238,7 @@ void LLFloater::setMinimized(BOOL minimize) if (minimize) { // minimized flag should be turned on before release focus - mMinimized = TRUE; + mMinimized = true; mExpandedRect = getRect(); // If the floater has been dragged while minimized in the @@ -1257,11 +1257,11 @@ void LLFloater::setMinimized(BOOL minimize) if (mButtonsEnabled[BUTTON_MINIMIZE]) { - mButtonsEnabled[BUTTON_MINIMIZE] = FALSE; - mButtonsEnabled[BUTTON_RESTORE] = TRUE; + mButtonsEnabled[BUTTON_MINIMIZE] = false; + mButtonsEnabled[BUTTON_RESTORE] = true; } - setBorderVisible(TRUE); + setBorderVisible(true); for(handle_set_iter_t dependent_it = mDependents.begin(); dependent_it != mDependents.end(); @@ -1272,11 +1272,11 @@ void LLFloater::setMinimized(BOOL minimize) { if (floaterp->isMinimizeable()) { - floaterp->setMinimized(TRUE); + floaterp->setMinimized(true); } else if (!floaterp->isMinimized()) { - floaterp->setVisible(FALSE); + floaterp->setVisible(false); } } } @@ -1288,16 +1288,16 @@ void LLFloater::setMinimized(BOOL minimize) { if (mResizeBar[i] != NULL) { - mResizeBar[i]->setEnabled(FALSE); + mResizeBar[i]->setEnabled(false); } if (mResizeHandle[i] != NULL) { - mResizeHandle[i]->setEnabled(FALSE); + mResizeHandle[i]->setEnabled(false); } } // Reshape *after* setting mMinimized - reshape( minimized_width, floater_header_size, TRUE); + reshape( minimized_width, floater_header_size, true); } else { @@ -1313,8 +1313,8 @@ void LLFloater::setMinimized(BOOL minimize) setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom ); if (mButtonsEnabled[BUTTON_RESTORE]) { - mButtonsEnabled[BUTTON_MINIMIZE] = TRUE; - mButtonsEnabled[BUTTON_RESTORE] = FALSE; + mButtonsEnabled[BUTTON_MINIMIZE] = true; + mButtonsEnabled[BUTTON_RESTORE] = false; } // show dependent floater @@ -1325,8 +1325,8 @@ void LLFloater::setMinimized(BOOL minimize) LLFloater* floaterp = dependent_it->get(); if (floaterp) { - floaterp->setMinimized(FALSE); - floaterp->setVisible(TRUE); + floaterp->setMinimized(false); + floaterp->setVisible(true); } } @@ -1342,10 +1342,10 @@ void LLFloater::setMinimized(BOOL minimize) } } - mMinimized = FALSE; + mMinimized = false; setFrontmost(); // Reshape *after* setting mMinimized - reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE ); + reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), true ); } make_ui_sound("UISndWindowClose"); @@ -1353,7 +1353,7 @@ void LLFloater::setMinimized(BOOL minimize) applyTitle (); } -void LLFloater::setFocus( BOOL b ) +void LLFloater::setFocus( bool b ) { if (b && getIsChrome()) { @@ -1361,7 +1361,7 @@ void LLFloater::setFocus( BOOL b ) } LLView* last_focus = gFocusMgr.getLastFocusForGroup(this); // a descendent already has focus - BOOL child_had_focus = hasFocus(); + bool child_had_focus = hasFocus(); // give focus to first valid descendent LLPanel::setFocus(b); @@ -1384,7 +1384,7 @@ void LLFloater::setFocus( BOOL b ) last_focus->isInVisibleChain()) { // *FIX: should handle case where focus doesn't stick - last_focus->setFocus(TRUE); + last_focus->setFocus(true); } } updateTransparency(b ? TT_ACTIVE : TT_INACTIVE); @@ -1399,15 +1399,15 @@ void LLFloater::setRect(const LLRect &rect) } // virtual -void LLFloater::setIsChrome(BOOL is_chrome) +void LLFloater::setIsChrome(bool is_chrome) { // chrome floaters don't take focus at all if (is_chrome) { // remove focus if we're changing to chrome - setFocus(FALSE); + setFocus(false); // can't Ctrl-Tab to "chrome" floaters - setFocusRoot(FALSE); + setFocusRoot(false); mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome))); } @@ -1415,7 +1415,7 @@ void LLFloater::setIsChrome(BOOL is_chrome) } // Change the draw style to account for the foreground state. -void LLFloater::setForeground(BOOL front) +void LLFloater::setForeground(bool front) { if (front != mForeground) { @@ -1460,13 +1460,13 @@ void LLFloater::setHost(LLMultiFloater* host) // add tear off button if (mCanTearOff) { - mButtonsEnabled[BUTTON_TEAR_OFF] = TRUE; + mButtonsEnabled[BUTTON_TEAR_OFF] = true; } } else if (!mHostHandle.isDead() && !host) { mButtonScale = 1.f; - //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE; + //mButtonsEnabled[BUTTON_TEAR_OFF] = false; } if (host) { @@ -1501,7 +1501,7 @@ void LLFloater::moveResizeHandlesToFront() } /*virtual*/ -BOOL LLFloater::isFrontmost() +bool LLFloater::isFrontmost() { LLFloaterView* floater_view = getParentByType(); return getVisible() @@ -1509,7 +1509,7 @@ BOOL LLFloater::isFrontmost() && floater_view->getFrontmost() == this); } -void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition) +void LLFloater::addDependentFloater(LLFloater* floaterp, bool reposition) { mDependents.insert(floaterp->getHandle()); floaterp->mDependeeHandle = getHandle(); @@ -1519,7 +1519,7 @@ void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition) floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp)); floaterp->setSnapTarget(getHandle()); } - gFloaterView->adjustToFitScreen(floaterp, FALSE, TRUE); + gFloaterView->adjustToFitScreen(floaterp, false, true); if (floaterp->isFrontmost()) { // make sure to bring self and sibling floaters to front @@ -1527,7 +1527,7 @@ void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition) } } -void LLFloater::addDependentFloater(LLHandle dependent, BOOL reposition) +void LLFloater::addDependentFloater(LLHandle dependent, bool reposition) { LLFloater* dependent_floaterp = dependent.get(); if(dependent_floaterp) @@ -1542,7 +1542,7 @@ void LLFloater::removeDependentFloater(LLFloater* floaterp) floaterp->mDependeeHandle = LLHandle(); } -BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index) +bool LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index) { if( mButtonsEnabled[index] ) { @@ -1555,10 +1555,10 @@ BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index my_butt->handleMouseDown(local_x, local_y, mask)) { // the button handled it - return TRUE; + return true; } } - return FALSE; + return false; } bool LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks) @@ -1652,23 +1652,23 @@ void LLFloater::bringToFront( S32 x, S32 y ) // virtual -void LLFloater::setVisibleAndFrontmost(BOOL take_focus,const LLSD& key) +void LLFloater::setVisibleAndFrontmost(bool take_focus,const LLSD& key) { LLUIUsage::instance().logFloater(getInstanceName()); LLMultiFloater* hostp = getHost(); if (hostp) { - hostp->setVisible(TRUE); + hostp->setVisible(true); hostp->setFrontmost(take_focus); } else { - setVisible(TRUE); + setVisible(true); setFrontmost(take_focus); } } -void LLFloater::setFrontmost(BOOL take_focus, BOOL restore) +void LLFloater::setFrontmost(bool take_focus, bool restore) { LLMultiFloater* hostp = getHost(); if (hostp) @@ -1703,7 +1703,7 @@ void LLFloater::setCanDock(bool b) } else { - mButtonsEnabled[BUTTON_DOCK] = FALSE; + mButtonsEnabled[BUTTON_DOCK] = false; } } updateTitleButtons(); @@ -1718,7 +1718,7 @@ void LLFloater::setDocked(bool docked, bool pop_on_undock) if (mDocked) { - setMinimized(FALSE); + setMinimized(false); mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; } @@ -1760,9 +1760,9 @@ void LLFloater::onClickTearOff(LLFloater* self) new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight()); self->setRect(new_rect); } - gFloaterView->adjustToFitScreen(self, FALSE); + gFloaterView->adjustToFitScreen(self, false); // give focus to new window to keep continuity for the user - self->setFocus(TRUE); + self->setFocus(true); self->setTornOff(true); } else //Attach to parent. @@ -1774,7 +1774,7 @@ void LLFloater::onClickTearOff(LLFloater* self) { self->storeRectControl(); } - self->setMinimized(FALSE); // to reenable minimize button if it was minimized + self->setMinimized(false); // to reenable minimize button if it was minimized new_host->showFloater(self); // make sure host is visible new_host->openFloater(new_host->getKey()); @@ -1906,7 +1906,7 @@ void LLFloater::draw() const LLFontGL* font = LLFontGL::getFontSansSerif(); LLRect r = getRect(); gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - font->getLineHeight() - 1, - titlebar_focus_color % alpha, 0, TRUE); + titlebar_focus_color % alpha, 0, true); } } } @@ -1919,13 +1919,13 @@ void LLFloater::draw() { LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); // is this button a direct descendent and not a nested widget (e.g. checkbox)? - BOOL focus_is_child_button = dynamic_cast(focus_ctrl) != NULL && dynamic_cast(focus_ctrl)->getParent() == this; + bool focus_is_child_button = dynamic_cast(focus_ctrl) != NULL && dynamic_cast(focus_ctrl)->getParent() == this; // only enable default button when current focus is not a button getDefaultButton()->setBorderEnabled(!focus_is_child_button); } else { - getDefaultButton()->setBorderEnabled(FALSE); + getDefaultButton()->setBorderEnabled(false); } } if (isMinimized()) @@ -1934,7 +1934,7 @@ void LLFloater::draw() { drawChild(mButtons[i]); } - drawChild(mDragHandle, 0, 0, TRUE); + drawChild(mDragHandle, 0, 0, true); } else { @@ -1949,7 +1949,7 @@ void LLFloater::draw() LLFloater* old_host = mLastHostHandle.get(); if (!old_host) { - setCanTearOff(FALSE); + setCanTearOff(false); } } } @@ -1999,14 +1999,14 @@ void LLFloater::updateTransparency(ETypeTransparency transparency_type) updateTransparency(this, transparency_type); } -void LLFloater::setCanMinimize(BOOL can_minimize) +void LLFloater::setCanMinimize(bool can_minimize) { // if removing minimize/restore button programmatically, // go ahead and unminimize floater mCanMinimize = can_minimize; if (!can_minimize) { - setMinimized(FALSE); + setMinimized(false); } mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized(); @@ -2015,7 +2015,7 @@ void LLFloater::setCanMinimize(BOOL can_minimize) updateTitleButtons(); } -void LLFloater::setCanClose(BOOL can_close) +void LLFloater::setCanClose(bool can_close) { mCanClose = can_close; mButtonsEnabled[BUTTON_CLOSE] = can_close; @@ -2023,7 +2023,7 @@ void LLFloater::setCanClose(BOOL can_close) updateTitleButtons(); } -void LLFloater::setCanTearOff(BOOL can_tear_off) +void LLFloater::setCanTearOff(bool can_tear_off) { mCanTearOff = can_tear_off; mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead(); @@ -2032,23 +2032,23 @@ void LLFloater::setCanTearOff(BOOL can_tear_off) } -void LLFloater::setCanResize(BOOL can_resize) +void LLFloater::setCanResize(bool can_resize) { mResizable = can_resize; enableResizeCtrls(can_resize); } -void LLFloater::setCanDrag(BOOL can_drag) +void LLFloater::setCanDrag(bool can_drag) { // if we delete drag handle, we no longer have access to the floater's title // so just enable/disable it if (!can_drag && mDragHandle->getEnabled()) { - mDragHandle->setEnabled(FALSE); + mDragHandle->setEnabled(false); } else if (can_drag && !mDragHandle->getEnabled()) { - mDragHandle->setEnabled(TRUE); + mDragHandle->setEnabled(true); } } @@ -2126,13 +2126,13 @@ void LLFloater::updateTitleButtons() buttons_rect.mLeft = btn_rect.mLeft; } mButtons[i]->setRect(btn_rect); - mButtons[i]->setVisible(TRUE); + mButtons[i]->setVisible(true); // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater mButtons[i]->setTabStop(i == BUTTON_RESTORE); } else { - mButtons[i]->setVisible(FALSE); + mButtons[i]->setVisible(false); } } if (mDragHandle) @@ -2326,7 +2326,7 @@ static LLDefaultChildRegistry::Register r("floater_view"); LLFloaterView::LLFloaterView (const Params& p) : LLUICtrl (p), - mFocusCycleMode(FALSE), + mFocusCycleMode(false), mMinimizePositionVOffset(0), mSnapOffsetBottom(0), mSnapOffsetRight(0), @@ -2336,7 +2336,7 @@ LLFloaterView::LLFloaterView (const Params& p) } // By default, adjust vertical. -void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLFloaterView::reshape(S32 width, S32 height, bool called_from_parent) { LLView::reshape(width, height, called_from_parent); @@ -2406,7 +2406,7 @@ void LLFloaterView::restoreAll() LLFloater* floaterp = dynamic_cast(*child_it); if (floaterp) { - floaterp->setMinimized(FALSE); + floaterp->setMinimized(false); } } @@ -2481,7 +2481,7 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF } -void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore) +void LLFloaterView::bringToFront(LLFloater* child, bool give_focus, bool restore) { if (!child) return; @@ -2490,7 +2490,7 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore { if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) { - child->setFocus(TRUE); + child->setFocus(true); } return; } @@ -2543,7 +2543,7 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore // always unminimize dependee, but allow dependents to stay minimized if (!floaterp->isDependent()) { - floaterp->setMinimized(FALSE); + floaterp->setMinimized(false); } } floaters_to_move.clear(); @@ -2568,12 +2568,12 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore if(restore) { - child->setMinimized(FALSE); + child->setMinimized(false); } if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) { - child->setFocus(TRUE); + child->setFocus(true); // floater did not take focus, so relinquish focus to world if (!child->hasFocus()) { @@ -2594,7 +2594,7 @@ void LLFloaterView::highlightFocusedFloater() continue; } - BOOL floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater); + bool floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater); for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); dependent_it != floater->mDependents.end(); ++dependent_it) @@ -2602,7 +2602,7 @@ void LLFloaterView::highlightFocusedFloater() LLFloater* dependent_floaterp = dependent_it->get(); if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp)) { - floater_or_dependent_has_focus = TRUE; + floater_or_dependent_has_focus = true; } } @@ -2648,7 +2648,7 @@ void LLFloaterView::unhighlightFocusedFloater() { LLFloater *floater = (LLFloater *)(*child_it); - floater->setForeground(FALSE); + floater->setForeground(false); } } @@ -2657,7 +2657,7 @@ void LLFloaterView::focusFrontFloater() LLFloater* floaterp = getFrontmost(); if (floaterp) { - floaterp->setFocus(TRUE); + floaterp->setFocus(true); } } @@ -2677,7 +2677,7 @@ void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom) row -= floater_header_size ) //loop rows { - bool foundGap = TRUE; + bool foundGap = true; for(child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) //loop floaters @@ -2694,7 +2694,7 @@ void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom) { // needs the check for off grid. can't drag, // but window resize makes them off - foundGap = FALSE; + foundGap = false; break; } } @@ -2796,7 +2796,7 @@ void LLFloaterView::showHiddenFloaters() mHiddenFloaters.clear(); } -BOOL LLFloaterView::allChildrenClosed() +bool LLFloaterView::allChildrenClosed() { // see if there are any visible floaters (some floaters "close" // by setting themselves invisible) @@ -2830,7 +2830,7 @@ void LLFloaterView::refresh() LLRect snap_rect = getSnapRect(); if (snap_rect != mLastSnapRect) { - reshape(getRect().getWidth(), getRect().getHeight(), TRUE); + reshape(getRect().getWidth(), getRect().getHeight(), true); } // Constrain children to be entirely on the screen @@ -2845,7 +2845,7 @@ void LLFloaterView::refresh() } } -void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside, BOOL snap_in_toolbars/* = false*/) +void LLFloaterView::adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars/* = false*/) { if (floater->getParent() != this) { @@ -3025,7 +3025,7 @@ void LLFloaterView::syncFloaterTabOrder() if( !gFocusMgr.childHasKeyboardFocus( modal_dialog ) ) { - modal_dialog->setFocus(TRUE); + modal_dialog->setFocus(true); } if( !gFocusMgr.childHasMouseCapture( modal_dialog ) ) @@ -3041,7 +3041,7 @@ void LLFloaterView::syncFloaterTabOrder() LLFloater* floaterp = dynamic_cast(*child_it); if (gFocusMgr.childHasKeyboardFocus(floaterp)) { - bringToFront(floaterp, FALSE); + bringToFront(floaterp, false); break; } } @@ -3081,7 +3081,7 @@ S32 LLFloaterView::getZOrder(LLFloater* child) return rv; } -void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list) +void LLFloaterView::pushVisibleAll(bool visible, const skip_list_t& skip_list) { for (child_list_const_iter_t child_iter = getChildList()->begin(); child_iter != getChildList()->end(); ++child_iter) @@ -3294,7 +3294,7 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str setupParamsForExport(output_params, parent); output_node->setName(node->getName()->mString); parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); - return TRUE; + return true; } LLUICtrlFactory::instance().pushFileName(xml_filename); @@ -3303,7 +3303,7 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str { LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL; - return FALSE; + return false; } Params referenced_params; @@ -3373,7 +3373,7 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str setRect(rect); } - BOOL result; + bool result; result = postBuild(); if (!result) @@ -3382,7 +3382,7 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str } applyRectControl(); // If we have a saved rect control, apply it - gFloaterView->adjustToFitScreen(this, FALSE); // Floaters loaded from XML should all fit on screen + gFloaterView->adjustToFitScreen(this, false); // Floaters loaded from XML should all fit on screen moveResizeHandlesToFront(); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 99ec77fa4d..bc315785d3 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -46,20 +46,20 @@ class LLMultiFloater; class LLFloater; -const BOOL RESIZE_YES = TRUE; -const BOOL RESIZE_NO = FALSE; +const bool RESIZE_YES = true; +const bool RESIZE_NO = false; -const BOOL DRAG_ON_TOP = FALSE; -const BOOL DRAG_ON_LEFT = TRUE; +const bool DRAG_ON_TOP = false; +const bool DRAG_ON_LEFT = true; -const BOOL MINIMIZE_YES = TRUE; -const BOOL MINIMIZE_NO = FALSE; +const bool MINIMIZE_YES = true; +const bool MINIMIZE_NO = false; -const BOOL CLOSE_YES = TRUE; -const BOOL CLOSE_NO = FALSE; +const bool CLOSE_YES = true; +const bool CLOSE_NO = false; -const BOOL ADJUST_VERTICAL_YES = TRUE; -const BOOL ADJUST_VERTICAL_NO = FALSE; +const bool ADJUST_VERTICAL_YES = true; +const bool ADJUST_VERTICAL_NO = false; namespace LLFloaterEnums { @@ -219,13 +219,13 @@ public: bool initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node = NULL); /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); - /*virtual*/ BOOL canSnapTo(const LLView* other_view); + /*virtual*/ bool canSnapTo(const LLView* other_view); /*virtual*/ void setSnappedTo(const LLView* snap_view); - /*virtual*/ void setFocus( BOOL b ); - /*virtual*/ void setIsChrome(BOOL is_chrome); + /*virtual*/ void setFocus( bool b ); + /*virtual*/ void setIsChrome(bool is_chrome); /*virtual*/ void setRect(const LLRect &rect); - void setIsSingleInstance(BOOL is_single_instance); - BOOL getIsSingleInstance() { return mSingleInstance; } + void setIsSingleInstance(bool is_single_instance); + bool getIsSingleInstance() { return mSingleInstance; } void initFloater(const Params& p); @@ -237,7 +237,7 @@ public: // Close the floater or its host. Use when hidding or toggling a floater instance. virtual void closeHostedFloater(); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); // Release keyboard and mouse focus void releaseFocus(); @@ -254,13 +254,13 @@ public: std::string getTitle() const; void setShortTitle( const std::string& short_title ); std::string getShortTitle() const; - virtual void setMinimized(BOOL b); + virtual void setMinimized(bool b); void moveResizeHandlesToFront(); - void addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE); - void addDependentFloater(LLHandle dependent_handle, BOOL reposition = TRUE); + void addDependentFloater(LLFloater* dependent, bool reposition = true); + void addDependentFloater(LLHandle dependent_handle, bool reposition = true); LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); } void removeDependentFloater(LLFloater* dependent); - BOOL isMinimized() const { return mMinimized; } + bool isMinimized() const { return mMinimized; } /// isShown() differs from getVisible() in that isShown() also considers /// isMinimized(). isShown() is true only if visible and not minimized. bool isShown() const; @@ -269,17 +269,17 @@ public: static bool isShown(const LLFloater* floater); static bool isVisible(const LLFloater* floater); static bool isMinimized(const LLFloater* floater); - BOOL isFirstLook() { return mFirstLook; } // EXT-2653: This function is necessary to prevent overlapping for secondary showed toasts - virtual BOOL isFrontmost(); - BOOL isDependent() { return !mDependeeHandle.isDead(); } - void setCanMinimize(BOOL can_minimize); - void setCanClose(BOOL can_close); - void setCanTearOff(BOOL can_tear_off); - virtual void setCanResize(BOOL can_resize); - void setCanDrag(BOOL can_drag); + bool isFirstLook() { return mFirstLook; } // EXT-2653: This function is necessary to prevent overlapping for secondary showed toasts + virtual bool isFrontmost(); + bool isDependent() { return !mDependeeHandle.isDead(); } + void setCanMinimize(bool can_minimize); + void setCanClose(bool can_close); + void setCanTearOff(bool can_tear_off); + virtual void setCanResize(bool can_resize); + void setCanDrag(bool can_drag); bool getCanDrag(); void setHost(LLMultiFloater* host); - BOOL isResizable() const { return mResizable; } + bool isResizable() const { return mResizable; } void setResizeLimits( S32 min_width, S32 min_height ); void getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; } @@ -309,16 +309,16 @@ public: // This cannot be "const" until all derived floater canClose() // methods are const as well. JC - virtual BOOL canClose() { return TRUE; } + virtual bool canClose() { return true; } - /*virtual*/ void setVisible(BOOL visible); // do not override - /*virtual*/ void onVisibilityChange ( BOOL new_visibility ); // do not override + /*virtual*/ void setVisible(bool visible); // do not override + /*virtual*/ void onVisibilityChange ( bool new_visibility ); // do not override - void setFrontmost(BOOL take_focus = TRUE, BOOL restore = TRUE); - virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD()); + void setFrontmost(bool take_focus = true, bool restore = true); + virtual void setVisibleAndFrontmost(bool take_focus=true, const LLSD& key = LLSD()); // Defaults to false. - virtual BOOL canSaveAs() const { return FALSE; } + virtual bool canSaveAs() const { return false; } virtual void saveAs() {} @@ -391,8 +391,8 @@ protected: void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized const LLRect& getExpandedRect() const { return mExpandedRect; } - void setAutoFocus(BOOL focus) { mAutoFocus = focus; } // whether to automatically take focus when opened - BOOL getAutoFocus() const { return mAutoFocus; } + void setAutoFocus(bool focus) { mAutoFocus = focus; } // whether to automatically take focus when opened + bool getAutoFocus() const { return mAutoFocus; } LLDragHandle* getDragHandle() const { return mDragHandle; } void destroy(); // Don't call this directly. You probably want to call closeFloater() @@ -411,7 +411,7 @@ protected: F32 contex_cone_out_alpha = CONTEXT_CONE_OUT_ALPHA); private: - void setForeground(BOOL b); // called only by floaterview + void setForeground(bool b); // called only by floaterview void cleanupHandles(); // remove handles to dead floaters void createMinimizeButton(); void buildButtons(const Params& p); @@ -428,7 +428,7 @@ private: */ static std::string getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome); - BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index); + bool offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index); void addResizeCtrls(); void layoutResizeCtrls(); void addDragHandle(); @@ -474,16 +474,16 @@ private: LLUIString mTitle; LLUIString mShortTitle; - BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater + bool mSingleInstance; // true if there is only ever one instance of the floater bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it bool mIsReuseInitialized; // true if mReuseInstance already set from parameters std::string mInstanceName; // Store the instance name so we can remove ourselves from the list - BOOL mCanTearOff; - BOOL mCanMinimize; - BOOL mCanClose; - BOOL mDragOnLeft; - BOOL mResizable; + bool mCanTearOff; + bool mCanMinimize; + bool mCanClose; + bool mDragOnLeft; + bool mResizable; LLFloaterEnums::EOpenPositioning mPositioning; LLCoordFloater mPosition; @@ -493,12 +493,12 @@ private: S32 mHeaderHeight; // height in pixels of header for title, drag bar S32 mLegacyHeaderHeight;// HACK see initFloaterXML() - BOOL mMinimized; - BOOL mForeground; + bool mMinimized; + bool mForeground; LLHandle mDependeeHandle; - BOOL mFirstLook; // TRUE if the _next_ time this floater is visible will be the first time in the session that it is visible. + bool mFirstLook; // true if the _next_ time this floater is visible will be the first time in the session that it is visible. typedef std::set > handle_set_t; typedef std::set >::iterator handle_set_iter_t; @@ -506,7 +506,7 @@ private: bool mButtonsEnabled[BUTTON_COUNT]; F32 mButtonScale; - BOOL mAutoFocus; + bool mAutoFocus; LLHandle mSnappedTo; LLHandle mHostHandle; @@ -517,7 +517,7 @@ private: bool mTornOff; static LLMultiFloater* sHostp; - static BOOL sQuitting; + static bool sQuitting; static std::string sButtonNames[BUTTON_COUNT]; static std::string sButtonToolTips[BUTTON_COUNT]; static std::string sButtonToolTipsIndex[BUTTON_COUNT]; @@ -525,7 +525,7 @@ private: typedef void(*click_callback)(LLFloater*); static click_callback sButtonCallbacks[BUTTON_COUNT]; - BOOL mHasBeenDraggedWhileMinimized; + bool mHasBeenDraggedWhileMinimized; S32 mPreviousMinimizedBottom; S32 mPreviousMinimizedLeft; @@ -551,7 +551,7 @@ protected: public: - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); /*virtual*/ void draw(); /*virtual*/ LLRect getSnapRect() const; /*virtual*/ void refresh(); @@ -559,25 +559,25 @@ public: LLRect findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor ); // Given a child of gFloaterView, make sure this view can fit entirely onscreen. - void adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside, BOOL snap_in_toolbars = false); + void adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars = false); void setMinimizePositionVerticalOffset(S32 offset) { mMinimizePositionVOffset = offset; } void getMinimizePosition( S32 *left, S32 *bottom); void restoreAll(); // un-minimize all floaters typedef std::set skip_list_t; - void pushVisibleAll(BOOL visible, const skip_list_t& skip_list = skip_list_t()); + void pushVisibleAll(bool visible, const skip_list_t& skip_list = skip_list_t()); void popVisibleAll(const skip_list_t& skip_list = skip_list_t()); - void setCycleMode(BOOL mode) { mFocusCycleMode = mode; } - BOOL getCycleMode() const { return mFocusCycleMode; } - void bringToFront( LLFloater* child, BOOL give_focus = TRUE, BOOL restore = TRUE ); + void setCycleMode(bool mode) { mFocusCycleMode = mode; } + bool getCycleMode() const { return mFocusCycleMode; } + void bringToFront( LLFloater* child, bool give_focus = true, bool restore = true ); void highlightFocusedFloater(); void unhighlightFocusedFloater(); void focusFrontFloater(); void destroyAllChildren(); // attempt to close all floaters void closeAllChildren(bool app_quitting); - BOOL allChildrenClosed(); + bool allChildrenClosed(); void shiftFloaters(S32 x_offset, S32 y_offset); void hideAllFloaters(); @@ -608,7 +608,7 @@ private: LLRect mToolbarBottomRect; LLRect mToolbarRightRect; LLHandle mSnapView; - BOOL mFocusCycleMode; + bool mFocusCycleMode; S32 mSnapOffsetBottom; S32 mSnapOffsetRight; S32 mMinimizePositionVOffset; diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 62c26709d8..d87e4ce70f 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -248,7 +248,7 @@ LLFloaterReg::const_instance_list_t& LLFloaterReg::getFloaterList(const std::str // Visibility Management //static -LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, BOOL focus) +LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, bool focus) { if( sBlockShowFloaters // see EXT-7090 @@ -259,7 +259,7 @@ LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, { instance->openFloater(key); if (focus) - instance->setFocus(TRUE); + instance->setFocus(true); } return instance; } @@ -288,7 +288,7 @@ bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key) } else { - return showInstance(name, key, TRUE) ? true : false; + return showInstance(name, key, true) ? true : false; } } @@ -332,7 +332,7 @@ void LLFloaterReg::hideVisibleInstances(const std::set& exceptions) for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) { LLFloater* floater = *iter; - floater->pushVisible(FALSE); + floater->pushVisible(false); } } } @@ -409,7 +409,7 @@ std::string LLFloaterReg::getBaseControlName(const std::string& name) std::string LLFloaterReg::declareVisibilityControl(const std::string& name) { std::string controlname = getVisibilityControlName(name); - LLFloater::getControlGroup()->declareBOOL(controlname, FALSE, + LLFloater::getControlGroup()->declareBOOL(controlname, false, llformat("Window Visibility for %s", name.c_str()), LLControlVariable::PERSIST_NONDFT); return controlname; @@ -419,7 +419,7 @@ std::string LLFloaterReg::declareVisibilityControl(const std::string& name) std::string LLFloaterReg::declareDockStateControl(const std::string& name) { std::string controlname = getDockStateControlName(name); - LLFloater::getControlGroup()->declareBOOL(controlname, TRUE, + LLFloater::getControlGroup()->declareBOOL(controlname, true, llformat("Window Docking state for %s", name.c_str()), LLControlVariable::PERSIST_NONDFT); return controlname; @@ -492,7 +492,7 @@ void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& { if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) { - host->setMinimized(FALSE); + host->setMinimized(false); instance->openFloater(key); instance->setVisibleAndFrontmost(true, key); } @@ -500,7 +500,7 @@ void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& { instance->openFloater(key); instance->setVisibleAndFrontmost(true, key); - instance->setFocus(TRUE); + instance->setFocus(true); } else { @@ -511,7 +511,7 @@ void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& { if (instance->isMinimized()) { - instance->setMinimized(FALSE); + instance->setMinimized(false); instance->setVisibleAndFrontmost(true, key); } else if (!instance->isShown()) @@ -552,7 +552,7 @@ void LLFloaterReg::showInstanceOrBringToFront(const LLSD& sdname, const LLSD& ke { if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) { - host->setMinimized(FALSE); + host->setMinimized(false); instance->openFloater(key); instance->setVisibleAndFrontmost(true, key); } @@ -560,14 +560,14 @@ void LLFloaterReg::showInstanceOrBringToFront(const LLSD& sdname, const LLSD& ke { instance->openFloater(key); instance->setVisibleAndFrontmost(true, key); - instance->setFocus(TRUE); + instance->setFocus(true); } } else { if (instance->isMinimized()) { - instance->setMinimized(FALSE); + instance->setMinimized(false); instance->setVisibleAndFrontmost(true, key); } else if (!instance->isShown()) diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index eaa59b1d6f..85d6ad6b12 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -102,7 +102,7 @@ public: // Visibility Management // return NULL if instance not found or can't create instance (no builder) - static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), BOOL focus = FALSE); + static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false); // Close a floater (may destroy or set invisible) // return false if can't find instance static bool hideInstance(const std::string& name, const LLSD& key = LLSD()); @@ -145,7 +145,7 @@ public: } template - static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), BOOL focus = FALSE) + static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false) { return dynamic_cast(showInstance(name, key, focus)); } diff --git a/indra/llui/llflyoutbutton.cpp b/indra/llui/llflyoutbutton.cpp index 4b3a0a5d21..392bfb8bf4 100644 --- a/indra/llui/llflyoutbutton.cpp +++ b/indra/llui/llflyoutbutton.cpp @@ -35,7 +35,7 @@ const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24; LLFlyoutButton::LLFlyoutButton(const Params& p) : LLComboBox(p), - mToggleState(FALSE), + mToggleState(false), mActionButton(NULL) { // Always use text box @@ -69,7 +69,7 @@ void LLFlyoutButton::draw() LLComboBox::draw(); } -void LLFlyoutButton::setToggleState(BOOL state) +void LLFlyoutButton::setToggleState(bool state) { mToggleState = state; } diff --git a/indra/llui/llflyoutbutton.h b/indra/llui/llflyoutbutton.h index 36998eba2e..15c7b4600f 100644 --- a/indra/llui/llflyoutbutton.h +++ b/indra/llui/llflyoutbutton.h @@ -56,13 +56,13 @@ protected: public: virtual void draw(); - void setToggleState(BOOL state); + void setToggleState(bool state); void onActionButtonClick(const LLSD& data); protected: LLButton* mActionButton; - BOOL mToggleState; + bool mToggleState; }; #endif // LL_LLFLYOUTBUTTON_H diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 7b0a6cbdae..c0fdcf8bf6 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -41,21 +41,21 @@ LLFocusableElement::LLFocusableElement() } // virtual -BOOL LLFocusableElement::handleKey(KEY key, MASK mask, BOOL called_from_parent) +bool LLFocusableElement::handleKey(KEY key, MASK mask, bool called_from_parent) { - return FALSE; + return false; } // virtual -BOOL LLFocusableElement::handleKeyUp(KEY key, MASK mask, BOOL called_from_parent) +bool LLFocusableElement::handleKeyUp(KEY key, MASK mask, bool called_from_parent) { - return FALSE; + return false; } // virtual -BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +bool LLFocusableElement::handleUnicodeChar(llwchar uni_char, bool called_from_parent) { - return FALSE; + return false; } // virtual @@ -96,12 +96,12 @@ void LLFocusableElement::onTopLost() if (mTopLostCallback) (*mTopLostCallback)(this); } -BOOL LLFocusableElement::hasFocus() const +bool LLFocusableElement::hasFocus() const { return gFocusMgr.getKeyboardFocus() == this; } -void LLFocusableElement::setFocus(BOOL b) +void LLFocusableElement::setFocus(bool b) { } @@ -149,9 +149,9 @@ LLFocusMgr::LLFocusMgr() mKeyboardFocus( NULL ), mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), - mKeystrokesOnly(FALSE), + mKeystrokesOnly(false), mTopCtrl( NULL ), - mAppHasFocus(TRUE), // Macs don't seem to notify us that we've gotten focus, so default to true + mAppHasFocus(true), // Macs don't seem to notify us that we've gotten focus, so default to true mImpl(new LLFocusMgr::Impl) { } @@ -186,7 +186,7 @@ void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) LLUI::getInstance()->removePopup(view); } -void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only) +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 @@ -269,7 +269,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL // releasing keyboard focus, move to the default. if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) { - mDefaultKeyboardFocus->setFocus(TRUE); + mDefaultKeyboardFocus->setFocus(true); } LLView* focus_subtree = dynamic_cast(mKeyboardFocus); @@ -301,23 +301,23 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL } -// Returns TRUE is parent or any descedent of parent has keyboard focus. -BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const +// Returns true is parent or any descedent of parent has keyboard focus. +bool LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const { LLView* focus_view = dynamic_cast(mKeyboardFocus); while( focus_view ) { if( focus_view == parent ) { - return TRUE; + return true; } focus_view = focus_view->getParent(); } - return FALSE; + return false; } -// Returns TRUE is parent or any descedent of parent is the mouse captor. -BOOL LLFocusMgr::childHasMouseCapture( 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 && dynamic_cast(mMouseCaptor) != NULL ) { @@ -326,12 +326,12 @@ BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const { if( captor_view == parent ) { - return TRUE; + return true; } captor_view = captor_view->getParent(); } } - return FALSE; + return false; } void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ) @@ -400,18 +400,18 @@ void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor } -BOOL LLFocusMgr::childIsTopCtrl( const LLView* parent ) const +bool LLFocusMgr::childIsTopCtrl( const LLView* parent ) const { LLView* top_view = (LLView*)mTopCtrl; while( top_view ) { if( top_view == parent ) { - return TRUE; + return true; } top_view = top_view->getParent(); } - return FALSE; + return false; } @@ -471,7 +471,7 @@ void LLFocusMgr::triggerFocusFlash() mFocusFlashTimer.reset(); } -void LLFocusMgr::setAppHasFocus(BOOL focus) +void LLFocusMgr::setAppHasFocus(bool focus) { if (!mAppHasFocus && focus) { diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 0e3d7d8e59..3276135faf 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -45,8 +45,8 @@ public: LLFocusableElement(); virtual ~LLFocusableElement(); - virtual void setFocus( BOOL b ); - virtual BOOL hasFocus() const; + virtual void setFocus( bool b ); + virtual bool hasFocus() const; typedef boost::signals2::signal focus_signal_t; @@ -56,9 +56,9 @@ public: 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 handleKeyUp(KEY key, MASK mask, BOOL called_from_parent); - virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); + virtual bool handleKeyUp(KEY key, MASK mask, bool called_from_parent); + virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); /** * If true this LLFocusableElement wants to receive KEYUP and KEYDOWN messages @@ -89,23 +89,23 @@ public: void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse. LLMouseHandler* getMouseCapture() const { return mMouseCaptor; } void removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ); - BOOL childHasMouseCapture( const LLView* parent ) const; + bool childHasMouseCapture( const LLView* parent ) const; // Keyboard Focus - void setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock = FALSE, BOOL keystrokes_only = FALSE); // new_focus = NULL to release the focus. + void setKeyboardFocus(LLFocusableElement* new_focus, bool lock = false, bool keystrokes_only = false); // new_focus = NULL to release the focus. LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; } LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; } - BOOL childHasKeyboardFocus( const LLView* parent ) const; + bool childHasKeyboardFocus( const LLView* parent ) const; void removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ); - BOOL getKeystrokesOnly() { return mKeystrokesOnly; } - void setKeystrokesOnly(BOOL keystrokes_only) { mKeystrokesOnly = keystrokes_only; } + bool getKeystrokesOnly() { return mKeystrokesOnly; } + void setKeystrokesOnly(bool keystrokes_only) { mKeystrokesOnly = keystrokes_only; } F32 getFocusFlashAmt() const; S32 getFocusFlashWidth() const { return ll_round(lerp(1.f, 3.f, getFocusFlashAmt())); } LLColor4 getFocusColor() const; void triggerFocusFlash(); - BOOL getAppHasFocus() const { return mAppHasFocus; } - void setAppHasFocus(BOOL focus); + bool getAppHasFocus() const { return mAppHasFocus; } + void setAppHasFocus(bool focus); LLView* getLastFocusForGroup(LLView* subtree_root) const; void clearLastFocusForGroup(LLView* subtree_root); @@ -119,13 +119,13 @@ public: void setTopCtrl(LLUICtrl* new_top); LLUICtrl* getTopCtrl() const { return mTopCtrl; } void removeTopCtrlWithoutCallback( const LLUICtrl* top_view ); - BOOL childIsTopCtrl( const LLView* parent ) const; + bool childIsTopCtrl( const LLView* parent ) const; // All Three void releaseFocusIfNeeded( LLView* top_view ); void lockFocus(); void unlockFocus(); - BOOL focusLocked() const { return mLockedView != NULL; } + bool focusLocked() const { return mLockedView != NULL; } bool keyboardFocusHasAccelerators() const; @@ -141,14 +141,14 @@ private: LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object LLFocusableElement* mLastKeyboardFocus; // who last had focus LLFocusableElement* mDefaultKeyboardFocus; - BOOL mKeystrokesOnly; + bool mKeystrokesOnly; // Top View LLUICtrl* mTopCtrl; LLFrameTimer mFocusFlashTimer; - BOOL mAppHasFocus; + bool mAppHasFocus; Impl * mImpl; }; diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index c9f74c63f8..0ac04e374d 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -84,12 +84,12 @@ F32 LLFolderView::sAutoOpenTime = 1.f; class LLCloseAllFoldersFunctor : public LLFolderViewFunctor { public: - LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; } + LLCloseAllFoldersFunctor(bool close) { mOpen = !close; } virtual ~LLCloseAllFoldersFunctor() {} virtual void doFolder(LLFolderViewFolder* folder); virtual void doItem(LLFolderViewItem* item); - BOOL mOpen; + bool mOpen; }; @@ -167,21 +167,21 @@ LLFolderView::LLFolderView(const Params& p) mAllowMultiSelect(p.allow_multiselect), mAllowDrag(p.allow_drag), mShowEmptyMessage(p.show_empty_message), - mShowFolderHierarchy(FALSE), + mShowFolderHierarchy(false), mRenameItem( NULL ), - mNeedsScroll( FALSE ), + mNeedsScroll( false ), mUseLabelSuffix(p.use_label_suffix), mSuppressFolderMenu(p.suppress_folder_menu), - mPinningSelectedItem(FALSE), - mNeedsAutoSelect( FALSE ), - mAutoSelectOverride(FALSE), - mNeedsAutoRename(FALSE), - mShowSelectionContext(FALSE), - mShowSingleSelection(FALSE), + mPinningSelectedItem(false), + mNeedsAutoSelect( false ), + mAutoSelectOverride(false), + mNeedsAutoRename(false), + mShowSelectionContext(false), + mShowSingleSelection(false), mArrangeGeneration(0), mSignalSelectCallback(0), mMinWidth(0), - mDragAndDropThisFrame(FALSE), + mDragAndDropThisFrame(false), mCallbackRegistrar(NULL), mEnableRegistrar(NULL), mUseEllipses(p.use_ellipses), @@ -205,7 +205,7 @@ LLFolderView::LLFolderView(const Params& p) mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); mAutoOpenCandidate = NULL; mAutoOpenTimer.stop(); - mKeyboardSelection = FALSE; + mKeyboardSelection = false; mIndentation = getParentFolder() ? getParentFolder()->getIndentation() + mLocalIndentation : 0; //clear label @@ -280,9 +280,9 @@ LLFolderView::~LLFolderView( void ) mViewModel = NULL; } -BOOL LLFolderView::canFocusChildren() const +bool LLFolderView::canFocusChildren() const { - return FALSE; + return false; } void LLFolderView::addFolder( LLFolderViewFolder* folder) @@ -293,7 +293,7 @@ void LLFolderView::addFolder( LLFolderViewFolder* folder) void LLFolderView::closeAllFolders() { // Close all the folders - setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); + setOpenArrangeRecursively(false, LLFolderViewFolder::RECURSE_DOWN); arrangeAll(); } @@ -303,7 +303,7 @@ void LLFolderView::openTopLevelFolders() iter != mFolders.end();) { folders_t::iterator fit = iter++; - (*fit)->setOpen(TRUE); + (*fit)->setOpen(true); } } @@ -343,7 +343,7 @@ void LLFolderView::filter( LLFolderViewFilter& filter ) getViewModelItem()->filter(filter); } -void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLFolderView::reshape(S32 width, S32 height, bool called_from_parent) { LLRect scroll_rect; if (mScrollContainer) @@ -360,7 +360,7 @@ void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) width = scroll_rect.getWidth(); } LLView::reshape(width, height, called_from_parent); - mReshapeSignal(mSelectedItems, FALSE); + mReshapeSignal(mSelectedItems, false); } void LLFolderView::addToSelectionList(LLFolderViewItem* item) @@ -371,9 +371,9 @@ void LLFolderView::addToSelectionList(LLFolderViewItem* item) } if (mSelectedItems.size()) { - mSelectedItems.back()->setIsCurSelection(FALSE); + mSelectedItems.back()->setIsCurSelection(false); } - item->setIsCurSelection(TRUE); + item->setIsCurSelection(true); mSelectedItems.push_back(item); } @@ -381,7 +381,7 @@ void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) { if (mSelectedItems.size()) { - mSelectedItems.back()->setIsCurSelection(FALSE); + mSelectedItems.back()->setIsCurSelection(false); } selected_items_t::iterator item_iter; @@ -398,7 +398,7 @@ void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) } if (mSelectedItems.size()) { - mSelectedItems.back()->setIsCurSelection(TRUE); + mSelectedItems.back()->setIsCurSelection(true); } } @@ -419,19 +419,19 @@ LLFolderView::selected_items_t& LLFolderView::getSelectedItems( void ) } // Record the selected item and pass it down the hierachy. -BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) +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; + return false; } if( selection && take_keyboard_focus) { - mParentPanel.get()->setFocus(TRUE); + mParentPanel.get()->setFocus(true); } // clear selection down here because change of keyboard focus can potentially @@ -443,7 +443,7 @@ BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, addToSelectionList(selection); } - BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); + bool rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); if(openitem && selection) { selection->getParentFolder()->requestArrange(); @@ -454,14 +454,14 @@ BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, return rv; } -BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) +bool LLFolderView::changeSelection(LLFolderViewItem* selection, bool selected) { - BOOL rv = FALSE; + bool rv = false; // can't select root folder if(!selection || selection == this) { - return FALSE; + return false; } if (!mAllowMultiSelect) @@ -478,7 +478,7 @@ BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) } } - BOOL on_list = (item_iter != mSelectedItems.end()); + bool on_list = (item_iter != mSelectedItems.end()); if(selected && !on_list) { @@ -510,7 +510,7 @@ void LLFolderView::sanitizeSelection() LLFolderViewItem* item = *item_iter; // ensure that each ancestor is open and potentially passes filtering - BOOL visible = false; + bool visible = false; if(item->getViewModelItem() != NULL) { visible = item->getViewModelItem()->potentiallyVisible(); // initialize from filter state for this item @@ -558,7 +558,7 @@ void LLFolderView::sanitizeSelection() std::vector::iterator item_it; for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it ) { - changeSelection(*item_it, FALSE); // toggle selection (also removes from list) + changeSelection(*item_it, false); // toggle selection (also removes from list) } // if nothing selected after prior constraints... @@ -596,7 +596,7 @@ void LLFolderView::sanitizeSelection() if (new_selection) { - setSelection(new_selection, FALSE, FALSE); + setSelection(new_selection, false, false); } } } @@ -664,12 +664,12 @@ void LLFolderView::draw() if (hasVisibleChildren()) { - mStatusTextBox->setVisible( FALSE ); + mStatusTextBox->setVisible( false ); } else if (mShowEmptyMessage) { mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty())); - mStatusTextBox->setVisible( TRUE ); + mStatusTextBox->setVisible( true ); // firstly reshape message textbox with current size. This is necessary to // LLTextBox::getTextPixelHeight works properly @@ -711,7 +711,7 @@ void LLFolderView::draw() // and arrow for the root folder LLView::draw(); - mDragAndDropThisFrame = FALSE; + mDragAndDropThisFrame = false; } void LLFolderView::finishRenamingItem( void ) @@ -830,7 +830,7 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) while (close_item && close_item != item->getParentFolder()) { mAutoOpenItems.pop(); - close_item->setOpenArrangeRecursively(FALSE); + close_item->setOpenArrangeRecursively(false); close_item = mAutoOpenItems.check(); } @@ -838,7 +838,7 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) mAutoOpenItems.push(item); - item->setOpen(TRUE); + item->setOpen(true); if(!item->isSingleFolderMode()) { LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); @@ -852,7 +852,7 @@ void LLFolderView::closeAutoOpenedFolders() while (mAutoOpenItems.check()) { LLFolderViewFolder* close_item = mAutoOpenItems.pop(); - close_item->setOpen(FALSE); + close_item->setOpen(false); } if (mAutoOpenCandidate) @@ -863,7 +863,7 @@ void LLFolderView::closeAutoOpenedFolders() mAutoOpenTimer.stop(); } -BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) +bool LLFolderView::autoOpenTest(LLFolderViewFolder* folder) { if (folder && mAutoOpenCandidate == folder) { @@ -877,10 +877,10 @@ BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) { autoOpenItem(folder); mAutoOpenTimer.stop(); - return TRUE; + return true; } } - return FALSE; + return false; } // otherwise new candidate, restart timer @@ -890,14 +890,14 @@ BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) } mAutoOpenCandidate = folder; mAutoOpenTimer.start(); - return FALSE; + return false; } -BOOL LLFolderView::canCopy() const +bool LLFolderView::canCopy() const { if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) { - return FALSE; + return false; } for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) @@ -905,10 +905,10 @@ BOOL LLFolderView::canCopy() const const LLFolderViewItem* item = *selected_it; if (!item->getViewModelItem()->isItemCopyable()) { - return FALSE; + return false; } } - return TRUE; + return true; } // copy selected item @@ -933,11 +933,11 @@ void LLFolderView::copy() mSearchString.clear(); } -BOOL LLFolderView::canCut() const +bool LLFolderView::canCut() const { if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) { - return FALSE; + return false; } for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) @@ -947,10 +947,10 @@ BOOL LLFolderView::canCut() const if (!listener || !listener->isItemRemovable()) { - return FALSE; + return false; } } - return TRUE; + return true; } void LLFolderView::cut() @@ -982,11 +982,11 @@ void LLFolderView::cut() mSearchString.clear(); } -BOOL LLFolderView::canPaste() const +bool LLFolderView::canPaste() const { if (mSelectedItems.empty()) { - return FALSE; + return false; } if(getVisible() && getEnabled()) @@ -1003,13 +1003,13 @@ BOOL LLFolderView::canPaste() const listener = folderp->getViewModelItem(); if (!listener || !listener->isClipboardPasteable()) { - return FALSE; + return false; } } } - return TRUE; + return true; } - return FALSE; + return false; } // paste selected item @@ -1069,17 +1069,17 @@ void LLFolderView::startRenamingSelectedItem( void ) mRenamer->setText(item->getName()); mRenamer->selectAll(); - mRenamer->setVisible( TRUE ); + mRenamer->setVisible( true ); // set focus will fail unless item is visible - mRenamer->setFocus( TRUE ); + mRenamer->setFocus( true ); mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); LLUI::getInstance()->addPopup(mRenamer); } } -BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) +bool LLFolderView::handleKeyHere( KEY key, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; // SL-51858: Key presses are not being passed to the Popup menu. // A proper fix is non-trivial so instead just close the menu. @@ -1094,7 +1094,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) case KEY_F2: mSearchString.clear(); startRenamingSelectedItem(); - handled = TRUE; + handled = true; break; case KEY_RETURN: @@ -1104,7 +1104,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { finishRenamingItem(); mSearchString.clear(); - handled = TRUE; + handled = true; } } break; @@ -1113,7 +1113,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if( mRenameItem && mRenamer->getVisible() ) { closeRenamer(); - handled = TRUE; + handled = true; } mSearchString.clear(); break; @@ -1124,7 +1124,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { mScrollContainer->pageUp(30); } - handled = TRUE; + handled = true; break; case KEY_PAGE_DOWN: @@ -1133,7 +1133,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { mScrollContainer->pageDown(30); } - handled = TRUE; + handled = true; break; case KEY_HOME: @@ -1142,7 +1142,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { mScrollContainer->goToTop(); } - handled = TRUE; + handled = true; break; case KEY_END: @@ -1157,14 +1157,14 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if((mSelectedItems.size() > 0) && mScrollContainer) { LLFolderViewItem* last_selected = getCurSelectedItem(); - BOOL shift_select = mask & MASK_SHIFT; + bool shift_select = mask & MASK_SHIFT; // don't shift select down to children of folders (they are implicitly selected through parent) LLFolderViewItem* next = last_selected->getNextOpenNode(!shift_select); if (!mKeyboardSelection || (!shift_select && (!next || next == last_selected))) { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; + setSelection(last_selected, false, true); + mKeyboardSelection = true; } if (shift_select) @@ -1174,12 +1174,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (next->isSelected()) { // shrink selection - changeSelection(last_selected, FALSE); + changeSelection(last_selected, false); } else if (last_selected->getParentFolder() == next->getParentFolder()) { // grow selection - changeSelection(next, TRUE); + changeSelection(next, true); } } } @@ -1193,11 +1193,11 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed { clearSelection(); - return TRUE; + return true; } - return FALSE; + return false; } - setSelection( next, FALSE, TRUE ); + setSelection( next, false, true ); } else { @@ -1205,14 +1205,14 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed { clearSelection(); - return TRUE; + return true; } - return FALSE; + return false; } } scrollToShowSelection(); mSearchString.clear(); - handled = TRUE; + handled = true; } break; @@ -1220,14 +1220,14 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if((mSelectedItems.size() > 0) && mScrollContainer) { LLFolderViewItem* last_selected = mSelectedItems.back(); - BOOL shift_select = mask & MASK_SHIFT; + bool shift_select = mask & MASK_SHIFT; // don't shift select down to children of folders (they are implicitly selected through parent) LLFolderViewItem* prev = last_selected->getPreviousOpenNode(!shift_select); if (!mKeyboardSelection || (!shift_select && prev == this)) { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; + setSelection(last_selected, false, true); + mKeyboardSelection = true; } if (shift_select) @@ -1237,12 +1237,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (prev->isSelected()) { // shrink selection - changeSelection(last_selected, FALSE); + changeSelection(last_selected, false); } else if (last_selected->getParentFolder() == prev->getParentFolder()) { // grow selection - changeSelection(prev, TRUE); + changeSelection(prev, true); } } } @@ -1256,18 +1256,18 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed { clearSelection(); - return TRUE; + return true; } - return FALSE; + return false; } - setSelection( prev, FALSE, TRUE ); + setSelection( prev, false, true ); } } scrollToShowSelection(); mSearchString.clear(); - handled = TRUE; + handled = true; } break; @@ -1275,9 +1275,9 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if(mSelectedItems.size()) { LLFolderViewItem* last_selected = getCurSelectedItem(); - last_selected->setOpen( TRUE ); + last_selected->setOpen( true ); mSearchString.clear(); - handled = TRUE; + handled = true; } break; @@ -1287,21 +1287,21 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) LLFolderViewItem* last_selected = getCurSelectedItem(); if(last_selected && last_selected->isSingleFolderMode()) { - handled = FALSE; + handled = false; break; } LLFolderViewItem* parent_folder = last_selected->getParentFolder(); if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) { - setSelection(parent_folder, FALSE, TRUE); + setSelection(parent_folder, false, true); } else { - last_selected->setOpen( FALSE ); + last_selected->setOpen( false ); } mSearchString.clear(); scrollToShowSelection(); - handled = TRUE; + handled = true; } break; } @@ -1365,7 +1365,7 @@ bool LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) return LLView::handleMouseDown( x, y, mask ); } -BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward) +bool LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, bool backward) { // get first selected item LLFolderViewItem* search_item = first_item; @@ -1382,7 +1382,7 @@ BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &searc } // search over all open nodes for first substring match (with wrapping) - BOOL found = FALSE; + bool found = false; LLFolderViewItem* original_search_item = search_item; do { @@ -1408,7 +1408,7 @@ BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &searc S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); if (!current_item_label.compare(0, search_string_length, upper_case_string)) { - found = TRUE; + found = true; break; } if (backward) @@ -1425,7 +1425,7 @@ BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &searc if (found) { - setSelection(search_item, FALSE, TRUE); + setSelection(search_item, false, true); scrollToShowSelection(); } @@ -1524,7 +1524,7 @@ bool LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) } // Add "--no options--" if the menu is completely blank. -BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const +bool LLFolderView::addNoOptions(LLMenuGL* menu) const { const std::string nooptions_str = "--no options--"; LLView *nooptions_item = NULL; @@ -1537,7 +1537,7 @@ BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const LLView *menu_item = (*itor); if (menu_item->getVisible()) { - return FALSE; + return false; } std::string name = menu_item->getName(); if (menu_item->getName() == nooptions_str) @@ -1547,11 +1547,11 @@ BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const } if (nooptions_item) { - nooptions_item->setVisible(TRUE); - nooptions_item->setEnabled(FALSE); - return TRUE; + nooptions_item->setVisible(true); + nooptions_item->setEnabled(false); + return true; } - return FALSE; + return false; } bool LLFolderView::handleHover( S32 x, S32 y, MASK mask ) @@ -1575,15 +1575,15 @@ void LLFolderView::setHoveredItem(LLFolderViewItem* itemp) } } -BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +bool LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) { - mDragAndDropThisFrame = TRUE; + mDragAndDropThisFrame = true; // have children handle it first - BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, + 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 @@ -1614,7 +1614,7 @@ void LLFolderView::scrollToShowSelection() { if ( mSelectedItems.size() ) { - mNeedsScroll = TRUE; + mNeedsScroll = true; } } @@ -1627,7 +1627,7 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr // don't scroll to items when mouse is being used to scroll/drag and drop if (gFocusMgr.childHasMouseCapture(mScrollContainer)) { - mNeedsScroll = FALSE; + mNeedsScroll = false; return; } @@ -1665,18 +1665,18 @@ LLRect LLFolderView::getVisibleRect() return visible_rect; } -BOOL LLFolderView::getShowSelectionContext() +bool LLFolderView::getShowSelectionContext() { if (mShowSelectionContext) { - return TRUE; + return true; } LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); if (menu && menu->getVisible()) { - return TRUE; + return true; } - return FALSE; + return false; } void LLFolderView::setShowSingleSelection(bool show) @@ -1707,7 +1707,7 @@ void LLFolderView::update() if (filter_object.isModified() && filter_object.isNotDefault() && mParentPanel.get()->getVisible()) { - mNeedsAutoSelect = TRUE; + mNeedsAutoSelect = true; } // Filter to determine visibility before arranging @@ -1734,7 +1734,7 @@ void LLFolderView::update() applyFunctorRecursively(functor); } - // Open filtered folders for folder views with mAutoSelectOverride=TRUE. + // Open filtered folders for folder views with mAutoSelectOverride=true. // Used by LLPlacesFolderView. if (filter_object.showAllResults()) { @@ -1747,7 +1747,7 @@ void LLFolderView::update() scrollToShowSelection(); } - BOOL filter_finished = mViewModel->contentsReady() + bool filter_finished = mViewModel->contentsReady() && (getViewModelItem()->passedFilter() || ( getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration() && !filter_modified)); @@ -1756,10 +1756,10 @@ void LLFolderView::update() || gFocusMgr.childHasMouseCapture(mParentPanel.get())) { // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process - mNeedsAutoSelect = FALSE; + mNeedsAutoSelect = false; } - BOOL is_visible = isInVisibleChain() || mForceArrange; + bool is_visible = isInVisibleChain() || mForceArrange; //Puts folders/items in proper positions // arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849) @@ -1784,7 +1784,7 @@ void LLFolderView::update() if (!mPinningSelectedItem && !mSelectedItems.empty()) { // lets pin it! - mPinningSelectedItem = TRUE; + mPinningSelectedItem = true; //Computes visible area const LLRect visible_content_rect = (mScrollContainer ? mScrollContainer->getVisibleContentRect() : LLRect()); @@ -1819,7 +1819,7 @@ void LLFolderView::update() // stop pinning selected item after folders stop rearranging if (!needsArrange()) { - mPinningSelectedItem = FALSE; + mPinningSelectedItem = false; } } @@ -1855,7 +1855,7 @@ void LLFolderView::update() } if (!needs_arrange || !is_visible) { - mNeedsScroll = FALSE; + mNeedsScroll = false; } } } @@ -1872,15 +1872,15 @@ void LLFolderView::update() if (mSignalSelectCallback) { //RN: we use keyboard focus as a proxy for user-explicit actions - BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); + bool take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); mSelectSignal(mSelectedItems, take_keyboard_focus); } - mSignalSelectCallback = FALSE; + mSignalSelectCallback = false; } } else { - mSignalSelectCallback = FALSE; + mSignalSelectCallback = false; } } @@ -1915,7 +1915,7 @@ void LLFolderView::updateRenamerPosition() 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 ); + mRenamer->reshape( width, height, true ); } } @@ -1927,9 +1927,9 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu) 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); + (*menu_itor)->setVisible(false); + (*menu_itor)->pushVisible(true); + (*menu_itor)->setEnabled(true); } // Successively filter out invalid options @@ -1995,7 +1995,7 @@ bool LLFolderView::selectFirstItem() { LLFolderViewItem* itemp = folder->getNextFromChild(0,true); if(itemp) - setSelection(itemp,FALSE,TRUE); + setSelection(itemp,false,true); return true; } @@ -2006,7 +2006,7 @@ bool LLFolderView::selectFirstItem() LLFolderViewItem* itemp = (*iit); if (itemp->getVisible()) { - setSelection(itemp,FALSE,TRUE); + setSelection(itemp,false,true); return true; } } @@ -2020,7 +2020,7 @@ bool LLFolderView::selectLastItem() LLFolderViewItem* itemp = (*iit); if (itemp->getVisible()) { - setSelection(itemp,FALSE,TRUE); + setSelection(itemp,false,true); return true; } } @@ -2032,7 +2032,7 @@ bool LLFolderView::selectLastItem() { LLFolderViewItem* itemp = folder->getPreviousFromChild(0,true); if(itemp) - setSelection(itemp,FALSE,TRUE); + setSelection(itemp,false,true); return true; } } @@ -2073,15 +2073,15 @@ void LLFolderView::onRenamerLost() { if (mRenamer && mRenamer->getVisible()) { - mRenamer->setVisible(FALSE); + mRenamer->setVisible(false); // will commit current name (which could be same as original name) - mRenamer->setFocus(FALSE); + mRenamer->setFocus(false); } if( mRenameItem ) { - setSelection( mRenameItem, TRUE ); + setSelection( mRenameItem, true ); mRenameItem = NULL; } } @@ -2089,17 +2089,17 @@ void LLFolderView::onRenamerLost() LLFolderViewItem* LLFolderView::getNextUnselectedItem() { LLFolderViewItem* last_item = *mSelectedItems.rbegin(); - LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); + LLFolderViewItem* new_selection = last_item->getNextOpenNode(false); while(new_selection && new_selection->isSelected()) { - new_selection = new_selection->getNextOpenNode(FALSE); + new_selection = new_selection->getNextOpenNode(false); } if (!new_selection) { - new_selection = last_item->getPreviousOpenNode(FALSE); + new_selection = last_item->getPreviousOpenNode(false); while (new_selection && (new_selection->isInSelection())) { - new_selection = new_selection->getPreviousOpenNode(FALSE); + new_selection = new_selection->getPreviousOpenNode(false); } } return new_selection; diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index b235de95e9..3c604503f6 100644 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -109,7 +109,7 @@ public: LLFolderView(const Params&); virtual ~LLFolderView( void ); - virtual BOOL canFocusChildren() const; + virtual bool canFocusChildren() const; virtual const LLFolderView* getRoot() const { return this; } virtual LLFolderView* getRoot() { return this; } @@ -120,7 +120,7 @@ public: LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; } const LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() const { return mGroupedItemModel; } - typedef boost::signals2::signal& items, BOOL user_action)> signal_t; + 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); } @@ -156,12 +156,12 @@ public: selected_items_t& getSelectedItems( void ); // Record the selected item and pass it down the hierarchy. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus = TRUE); + virtual bool setSelection(LLFolderViewItem* selection, bool openitem, + bool take_keyboard_focus = true); // 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 bool changeSelection(LLFolderViewItem* selection, bool selected); virtual std::set getSelectionList() const; @@ -172,7 +172,7 @@ public: void removeFromSelectionList(LLFolderViewItem* item); bool startDrag(); - void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; } + void setDragAndDropThisFrame() { mDragAndDropThisFrame = true; } void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } @@ -181,17 +181,17 @@ public: void autoOpenItem(LLFolderViewFolder* item); void closeAutoOpenedFolders(); - BOOL autoOpenTest(LLFolderViewFolder* item); - BOOL isOpen() const { return TRUE; } // root folder always open + bool autoOpenTest(LLFolderViewFolder* item); + bool isOpen() const { return true; } // root folder always open // Copy & paste - virtual BOOL canCopy() const; + virtual bool canCopy() const; virtual void copy(); - virtual BOOL canCut() const; + virtual bool canCut() const; virtual void cut(); - virtual BOOL canPaste() const; + virtual bool canPaste() const; virtual void paste(); LLFolderViewItem* getNextUnselectedItem(); @@ -200,20 +200,20 @@ public: void startRenamingSelectedItem( void ); // LLView functionality - ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); - /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); + ///*virtual*/ bool handleKey( KEY key, MASK mask, bool called_from_parent ); + /*virtual*/ bool handleKeyHere( KEY key, MASK mask ); /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); /*virtual*/ bool handleDoubleClick( S32 x, S32 y, MASK mask ); /*virtual*/ bool handleRightMouseDown( S32 x, S32 y, MASK mask ); /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + /*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 reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(FALSE); } + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(false); } virtual void draw(); virtual void deleteAllChildren(); @@ -223,22 +223,22 @@ public: void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } LLRect getVisibleRect(); - BOOL search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward); + bool search(LLFolderViewItem* first_item, const std::string &search_string, bool backward); void setShowSelectionContext(bool show) { mShowSelectionContext = show; } - BOOL getShowSelectionContext(); + bool getShowSelectionContext(); void setShowSingleSelection(bool show); - BOOL getShowSingleSelection() { return mShowSingleSelection; } + bool getShowSingleSelection() { return mShowSingleSelection; } F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } bool getUseEllipses() { return mUseEllipses; } S32 getSelectedCount() { return (S32)mSelectedItems.size(); } void update(); // needs to be called periodically (e.g. once per frame) - 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 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 showItemLinkOverlays() { return mShowItemLinkOverlays; } @@ -280,7 +280,7 @@ protected: bool selectFirstItem(); bool selectLastItem(); - BOOL addNoOptions(LLMenuGL* menu) const; + bool addNoOptions(LLMenuGL* menu) const; protected: @@ -384,14 +384,14 @@ public: class LLSelectFirstFilteredItem : public LLFolderViewFunctor { public: - LLSelectFirstFilteredItem() : mItemSelected(FALSE), mFolderSelected(FALSE) {} + LLSelectFirstFilteredItem() : mItemSelected(false), mFolderSelected(false) {} virtual ~LLSelectFirstFilteredItem() {} virtual void doFolder(LLFolderViewFolder* folder); virtual void doItem(LLFolderViewItem* item); - BOOL wasItemSelected() { return mItemSelected || mFolderSelected; } + bool wasItemSelected() { return mItemSelected || mFolderSelected; } protected: - BOOL mItemSelected; - BOOL mFolderSelected; + bool mItemSelected; + bool mFolderSelected; }; class LLOpenFilteredFolders : public LLFolderViewFunctor @@ -406,15 +406,15 @@ public: class LLSaveFolderState : public LLFolderViewFunctor { public: - LLSaveFolderState() : mApply(FALSE) {} + LLSaveFolderState() : mApply(false) {} virtual ~LLSaveFolderState() {} virtual void doFolder(LLFolderViewFolder* folder); virtual void doItem(LLFolderViewItem* item) {} - void setApply(BOOL apply); + void setApply(bool apply); void clearOpenFolders() { mOpenFolders.clear(); } protected: std::set mOpenFolders; - BOOL mApply; + bool mApply; }; class LLOpenFoldersWithSelection : public LLFolderViewFunctor diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 9069a95c9f..5956ae8b36 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -128,18 +128,18 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mSuffixNeedsRefresh(false), mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT), mParentFolder( NULL ), - mIsSelected( FALSE ), - mIsCurSelection( FALSE ), - mSelectPending(FALSE), + mIsSelected( false ), + mIsCurSelection( false ), + mSelectPending(false), mIsItemCut(false), mCutGeneration(0), mLabelStyle( LLFontGL::NORMAL ), - mHasVisibleChildren(FALSE), + mHasVisibleChildren(false), mLocalIndentation(p.folder_indentation), mIndentation(0), mItemHeight(p.item_height), mControlLabelRotation(0.f), - mDragAndDropTarget(FALSE), + mDragAndDropTarget(false), mLabel(p.name), mRoot(p.root), mViewModelItem(p.listener), @@ -185,7 +185,7 @@ LLFolderViewItem::~LLFolderViewItem() gFocusMgr.removeKeyboardFocusWithoutCallback(this); } -BOOL LLFolderViewItem::postBuild() +bool LLFolderViewItem::postBuild() { LLFolderViewModelItem& vmi = *getViewModelItem(); // getDisplayName() is expensive (due to internal getLabelSuffix() and name building) @@ -203,7 +203,7 @@ BOOL LLFolderViewItem::postBuild() // while LLFolderViewItem::arrange() updates visual part mSuffixNeedsRefresh = true; mLabelWidthDirty = true; - return TRUE; + return true; } LLFolderView* LLFolderViewItem::getRoot() @@ -216,21 +216,21 @@ const LLFolderView* LLFolderViewItem::getRoot() const return mRoot; } // Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. -BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) +bool LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) { LLFolderViewItem* root = this; while( root->mParentFolder ) { if( root->mParentFolder == potential_ancestor ) { - return TRUE; + return true; } root = root->mParentFolder; } - return FALSE; + return false; } -LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children) +LLFolderViewItem* LLFolderViewItem::getNextOpenNode(bool include_children) { if (!mParentFolder) { @@ -252,7 +252,7 @@ LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children) return itemp; } -LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) +LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(bool include_children) { if (!mParentFolder) { @@ -276,19 +276,19 @@ LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) return itemp; } -BOOL LLFolderViewItem::passedFilter(S32 filter_generation) +bool LLFolderViewItem::passedFilter(S32 filter_generation) { return getViewModelItem()->passedFilter(filter_generation); } -BOOL LLFolderViewItem::isPotentiallyVisible(S32 filter_generation) +bool LLFolderViewItem::isPotentiallyVisible(S32 filter_generation) { if (filter_generation < 0) { filter_generation = getFolderViewModel()->getFilter().getFirstSuccessGeneration(); } LLFolderViewModelItem* model = getViewModelItem(); - BOOL visible = model->passedFilter(filter_generation); + bool visible = model->passedFilter(filter_generation); if (model->getMarkedDirtyGeneration() >= filter_generation) { // unsure visibility state @@ -348,8 +348,8 @@ void LLFolderViewItem::refreshSuffix() } // Utility function for LLFolderView -void LLFolderViewItem::arrangeAndSet(BOOL set_selection, - BOOL take_keyboard_focus) +void LLFolderViewItem::arrangeAndSet(bool set_selection, + bool take_keyboard_focus) { LLFolderView* root = getRoot(); if (getParentFolder()) @@ -358,7 +358,7 @@ void LLFolderViewItem::arrangeAndSet(BOOL set_selection, } if(set_selection) { - getRoot()->setSelection(this, TRUE, take_keyboard_focus); + getRoot()->setSelection(this, true, take_keyboard_focus); if(root) { root->scrollToShowSelection(); @@ -373,7 +373,7 @@ std::set LLFolderViewItem::getSelectionList() const return selection; } -// addToFolder() returns TRUE if it succeeds. FALSE otherwise +// addToFolder() returns true if it succeeds. false otherwise void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder) { folder->addItem(this); @@ -443,7 +443,7 @@ S32 LLFolderViewItem::getTextPad() // means 'deselect' for a leaf item. Do this optimization after // multiple selection is implemented to make sure it all plays nice // together. -BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus) +bool LLFolderViewItem::setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus) { if (selection == this && !mIsSelected) { @@ -456,7 +456,7 @@ BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, return mIsSelected; } -BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected) +bool LLFolderViewItem::changeSelection(LLFolderViewItem* selection, bool selected) { if (selection == this) { @@ -468,31 +468,31 @@ BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selecte { selectItem(); } - return TRUE; + return true; } - return FALSE; + return false; } void LLFolderViewItem::deselectItem(void) { - mIsSelected = FALSE; + mIsSelected = false; } void LLFolderViewItem::selectItem(void) { - if (mIsSelected == FALSE) + if (mIsSelected == false) { - mIsSelected = TRUE; + mIsSelected = true; getViewModelItem()->selectItem(); } } -BOOL LLFolderViewItem::isMovable() +bool LLFolderViewItem::isMovable() { return getViewModelItem()->isItemMovable(); } -BOOL LLFolderViewItem::isRemovable() +bool LLFolderViewItem::isRemovable() { return getViewModelItem()->isItemRemovable(); } @@ -511,12 +511,12 @@ void LLFolderViewItem::destroyView() // Call through to the viewed object and return true if it can be // removed. -//BOOL LLFolderViewItem::removeRecursively(BOOL single_item) -BOOL LLFolderViewItem::remove() +//bool LLFolderViewItem::removeRecursively(bool single_item) +bool LLFolderViewItem::remove() { if(!isRemovable()) { - return FALSE; + return false; } return getViewModelItem()->removeItem(); } @@ -701,18 +701,18 @@ void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask) } } -BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +bool LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) { - BOOL handled = FALSE; - BOOL accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); + bool handled = false; + bool accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); handled = accepted; if (accepted) { - mDragAndDropTarget = TRUE; + mDragAndDropTarget = true; *accept = ACCEPT_YES_MULTI; } else @@ -773,7 +773,7 @@ void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const L return mIsItemCut; } -void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, +void LLFolderViewItem::drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor) { const S32 focus_top = getRect().getHeight(); @@ -787,7 +787,7 @@ void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeybo //--------------------------------------------------------------------------------// // Draw highlight for selected items // Note: Always render "current" item or flashing item, only render other selected - // items if mShowSingleSelection is FALSE. + // items if mShowSingleSelection is false. // if (isHighlightAllowed()) @@ -831,7 +831,7 @@ void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeybo focus_top, getRect().getWidth() - 2, focus_bottom, - focusOutlineColor, FALSE); + focusOutlineColor, false); } if (folder_open) @@ -840,14 +840,14 @@ void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeybo focus_bottom + 1, // overlap with bottom edge of above rect getRect().getWidth() - 2, 0, - focusOutlineColor, FALSE); + focusOutlineColor, false); if (showContent && !isFlashing()) { gl_rect_2d(FOCUS_LEFT, focus_bottom + 1, getRect().getWidth() - 2, 0, - bgColor, TRUE); + bgColor, true); } } } @@ -857,7 +857,7 @@ void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeybo focus_top, getRect().getWidth() - 2, focus_bottom, - mouseOverColor, FALSE); + mouseOverColor, false); } //--------------------------------------------------------------------------------// @@ -870,16 +870,16 @@ void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeybo focus_top, getRect().getWidth() - 2, focus_bottom, - bgColor, FALSE); + bgColor, false); if (folder_open) { gl_rect_2d(FOCUS_LEFT, focus_bottom + 1, // overlap with bottom edge of above rect getRect().getWidth() - 2, 0, - bgColor, FALSE); + bgColor, false); } - mDragAndDropTarget = FALSE; + mDragAndDropTarget = false; } } @@ -890,13 +890,13 @@ void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y // font->renderUTF8(mLabel, 0, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, TRUE); + S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, true); } void LLFolderViewItem::draw() { - const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); - const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled + const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false); + const bool filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : false); // If we have keyboard focus, draw selection filled const Params& default_params = LLUICtrlFactory::getDefaultParams(); const S32 TOP_PAD = default_params.item_top_pad; @@ -999,7 +999,7 @@ void LLFolderViewItem::draw() { suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); + S32_MAX, S32_MAX, &right_x, false ); } //--------------------------------------------------------------------------------// @@ -1013,7 +1013,7 @@ void LLFolderViewItem::draw() F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; font->renderUTF8( combined_string, filter_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); + filter_string_length, S32_MAX, &right_x, false ); } else { @@ -1023,7 +1023,7 @@ void LLFolderViewItem::draw() F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length); F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; font->renderUTF8( mLabel, filter_offset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x, FALSE ); + sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x, false ); } S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; @@ -1032,7 +1032,7 @@ void LLFolderViewItem::draw() S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length); F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; - suffix_font->renderUTF8( mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x, FALSE ); + suffix_font->renderUTF8( mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x, false ); } } @@ -1066,8 +1066,8 @@ bool LLFolderViewItem::isInSelection() const LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): LLFolderViewItem( p ), - mIsOpen(FALSE), - mExpanderHighlighted(FALSE), + mIsOpen(false), + mExpanderHighlighted(false), mCurHeight(0.f), mTargetHeight(0.f), mAutoOpenCountdown(0.f), @@ -1102,7 +1102,7 @@ LLFolderViewFolder::~LLFolderViewFolder( void ) gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() } -// addToFolder() returns TRUE if it succeeds. FALSE otherwise +// addToFolder() returns true if it succeeds. false otherwise void LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder) { folder->addFolder(this); @@ -1262,7 +1262,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) > ll_round(mCurHeight) + mMaxFolderItemOverlap) { // hide if beyond current folder height - (*fit)->setVisible(FALSE); + (*fit)->setVisible(false); } } @@ -1274,7 +1274,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) if (getRect().getHeight() - (*iit)->getRect().mBottom > ll_round(mCurHeight) + mMaxFolderItemOverlap) { - (*iit)->setVisible(FALSE); + (*iit)->setVisible(false); } } } @@ -1292,7 +1292,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) return ll_round(mTargetHeight); } -BOOL LLFolderViewFolder::needsArrange() +bool LLFolderViewFolder::needsArrange() { return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); } @@ -1304,17 +1304,17 @@ bool LLFolderViewFolder::descendantsPassedFilter(S32 filter_generation) // Passes selection information on to children and record selection // information if necessary. -BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) +bool LLFolderViewFolder::setSelection(LLFolderViewItem* selection, bool openitem, + bool take_keyboard_focus) { - BOOL rv = FALSE; + bool rv = false; if (selection == this) { if (!isSelected()) { selectItem(); } - rv = TRUE; + rv = true; } else { @@ -1322,9 +1322,9 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem { deselectItem(); } - rv = FALSE; + rv = false; } - BOOL child_selected = FALSE; + bool child_selected = false; for (folders_t::iterator iter = mFolders.begin(); iter != mFolders.end();) @@ -1332,8 +1332,8 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem folders_t::iterator fit = iter++; if((*fit)->setSelection(selection, openitem, take_keyboard_focus)) { - rv = TRUE; - child_selected = TRUE; + rv = true; + child_selected = true; } } for (items_t::iterator iter = mItems.begin(); @@ -1342,13 +1342,13 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem items_t::iterator iit = iter++; if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) { - rv = TRUE; - child_selected = TRUE; + rv = true; + child_selected = true; } } if(openitem && child_selected && !mSingleFolderMode) { - setOpenArrangeRecursively(TRUE); + setOpenArrangeRecursively(true); } return rv; } @@ -1356,15 +1356,15 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem // This method is used to change the selection of an item. // Recursively traverse all children; if 'selection' is 'this' then change // the select status if necessary. -// Returns TRUE if the selection state of this folder, or of a child, was changed. -BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selected) +// Returns true if the selection state of this folder, or of a child, was changed. +bool LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, bool selected) { - BOOL rv = FALSE; + bool rv = false; if(selection == this) { if (isSelected() != selected) { - rv = TRUE; + rv = true; if (selected) { selectItem(); @@ -1382,7 +1382,7 @@ BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selec folders_t::iterator fit = iter++; if((*fit)->changeSelection(selection, selected)) { - rv = TRUE; + rv = true; } } for (items_t::iterator iter = mItems.begin(); @@ -1391,7 +1391,7 @@ BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selec items_t::iterator iit = iter++; if((*iit)->changeSelection(selection, selected)) { - rv = TRUE; + rv = true; } } return rv; @@ -1575,7 +1575,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) { - if (getRoot()->getAllowMultiSelect() == FALSE) return; + if (getRoot()->getAllowMultiSelect() == false) return; LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem(); if (cur_selected_item == NULL) @@ -1624,7 +1624,7 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) LLFolderView* root = getRoot(); - BOOL selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected + bool selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected // array always go from 'will be selected' to ' will be unselected', iterate // in opposite direction to simplify identification of 'point of origin' in @@ -1634,12 +1634,12 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) ++it) { LLFolderViewItem* item = *it; - BOOL selected = item->isSelected(); + bool selected = item->isSelected(); if (!selection_reverse && selected) { // it is our 'point of origin' where we shift/expand from // don't deselect it - selection_reverse = TRUE; + selection_reverse = true; } else { @@ -1650,11 +1650,11 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) if (selection_reverse) { // at some point we reversed selection, first element should be deselected - root->changeSelection(last_selected_item_from_cur, FALSE); + root->changeSelection(last_selected_item_from_cur, false); } // element we expand to should always be selected - root->changeSelection(new_selection, TRUE); + root->changeSelection(new_selection, true); } @@ -1712,11 +1712,11 @@ void LLFolderViewFolder::extractItem( LLFolderViewItem* item, bool deparent_mode removeChild(item); } -BOOL LLFolderViewFolder::isMovable() +bool LLFolderViewFolder::isMovable() { if( !(getViewModelItem()->isItemMovable()) ) { - return FALSE; + return false; } for (items_t::iterator iter = mItems.begin(); @@ -1725,7 +1725,7 @@ BOOL LLFolderViewFolder::isMovable() items_t::iterator iit = iter++; if(!(*iit)->isMovable()) { - return FALSE; + return false; } } @@ -1735,18 +1735,18 @@ BOOL LLFolderViewFolder::isMovable() folders_t::iterator fit = iter++; if(!(*fit)->isMovable()) { - return FALSE; + return false; } } - return TRUE; + return true; } -BOOL LLFolderViewFolder::isRemovable() +bool LLFolderViewFolder::isRemovable() { if( !(getViewModelItem()->isItemRemovable()) ) { - return FALSE; + return false; } for (items_t::iterator iter = mItems.begin(); @@ -1755,7 +1755,7 @@ BOOL LLFolderViewFolder::isRemovable() items_t::iterator iit = iter++; if(!(*iit)->isRemovable()) { - return FALSE; + return false; } } @@ -1765,10 +1765,10 @@ BOOL LLFolderViewFolder::isRemovable() folders_t::iterator fit = iter++; if(!(*fit)->isRemovable()) { - return FALSE; + return false; } } - return TRUE; + return true; } void LLFolderViewFolder::destroyRoot() @@ -1788,7 +1788,7 @@ void LLFolderViewFolder::addItem(LLFolderViewItem* item) mItems.push_back(item); item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); - item->setVisible(FALSE); + item->setVisible(false); addChild(item); @@ -1811,7 +1811,7 @@ void LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) mFolders.push_back(folder); folder->setOrigin(0, 0); folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); + folder->setVisible(false); // rearrange all descendants too, as our indentation level might have changed //folder->requestArrange(); //requestSort(); @@ -1842,7 +1842,7 @@ void LLFolderViewFolder::toggleOpen() } // Force a folder open or closed -void LLFolderViewFolder::setOpen(BOOL openitem) +void LLFolderViewFolder::setOpen(bool openitem) { if(mSingleFolderMode) { @@ -1859,9 +1859,9 @@ void LLFolderViewFolder::setOpen(BOOL openitem) } } -void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) +void LLFolderViewFolder::setOpenArrangeRecursively(bool openitem, ERecurseType recurse) { - BOOL was_open = isOpen(); + bool was_open = isOpen(); mIsOpen = openitem; if(!was_open && openitem) { @@ -1896,17 +1896,17 @@ void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType r } } -BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, - BOOL drop, +bool LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, + bool drop, EDragAndDropType c_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) { - BOOL accepted = mViewModelItem->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); + bool accepted = mViewModelItem->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); if (accepted) { - mDragAndDropTarget = TRUE; + mDragAndDropTarget = true; *accept = ACCEPT_YES_MULTI; } else @@ -1917,7 +1917,7 @@ BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, // drag and drop to child item, so clear pending auto-opens getRoot()->autoOpenTest(NULL); - return TRUE; + return true; } void LLFolderViewFolder::openItem( void ) @@ -1960,14 +1960,14 @@ void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) } // LLView functionality -BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, +bool LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) { - BOOL handled = FALSE; + bool handled = false; if (isOpen()) { @@ -1981,11 +1981,11 @@ BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFolderViewFolder" << LL_ENDL; } - return TRUE; + return true; } -BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, - BOOL drop, +bool LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -1995,14 +1995,14 @@ BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, { *accept = ACCEPT_NO; tooltip_msg = LLTrans::getString("TooltipOutboxCannotDropOnRoot"); - return TRUE; + return true; } - BOOL accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); + bool accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); if (accepted) { - mDragAndDropTarget = TRUE; + mDragAndDropTarget = true; *accept = ACCEPT_YES_MULTI; } else @@ -2015,7 +2015,7 @@ BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, getRoot()->autoOpenTest(this); } - return TRUE; + return true; } @@ -2145,19 +2145,19 @@ void LLFolderViewFolder::draw() LLView::draw(); } - mExpanderHighlighted = FALSE; + mExpanderHighlighted = false; } // this does prefix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children ) +LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, bool include_children ) { - BOOL found_item = FALSE; + bool found_item = false; LLFolderViewItem* result = NULL; // when not starting from a given item, start at beginning if(item == NULL) { - found_item = TRUE; + found_item = true; } // find current item among children @@ -2175,16 +2175,16 @@ LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, { if(item == (*fit)) { - found_item = TRUE; + found_item = true; // if we are on downwards traversal if (include_children && (*fit)->isOpen()) { // look for first descendant - return (*fit)->getNextFromChild(NULL, TRUE); + return (*fit)->getNextFromChild(NULL, true); } // otherwise advance to next folder ++fit; - include_children = TRUE; + include_children = true; break; } } @@ -2196,7 +2196,7 @@ LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, { if(item == (*iit)) { - found_item = TRUE; + found_item = true; // point to next item ++iit; break; @@ -2209,7 +2209,7 @@ LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, { // you should never call this method with an item that isn't a child // so we should always find something - llassert(FALSE); + llassert(false); return NULL; } @@ -2247,22 +2247,22 @@ LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, { // If there are no siblings or children to go to, recurse up one level in the tree // and skip children for this folder, as we've already discounted them - result = mParentFolder->getNextFromChild(this, FALSE); + result = mParentFolder->getNextFromChild(this, false); } return result; } // this does postfix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children ) +LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, bool include_children ) { - BOOL found_item = FALSE; + bool found_item = false; LLFolderViewItem* result = NULL; // when not starting from a given item, start at end if(item == NULL) { - found_item = TRUE; + found_item = true; } // find current item among children @@ -2280,7 +2280,7 @@ LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* it { if(item == (*iit)) { - found_item = TRUE; + found_item = true; // point to next item ++iit; break; @@ -2294,7 +2294,7 @@ LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* it { if(item == (*fit)) { - found_item = TRUE; + found_item = true; // point to next folder ++fit; break; @@ -2307,7 +2307,7 @@ LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* it { // you should never call this method with an item that isn't a child // so we should always find something - llassert(FALSE); + llassert(false); return NULL; } diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index d178aa40d5..9d6b90dc3b 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -158,21 +158,21 @@ protected: static LLFontGL* getLabelFontForStyle(U8 style); - BOOL mIsSelected; + bool mIsSelected; public: static void initClass(); static void cleanupClass(); - BOOL postBuild(); + bool postBuild(); virtual void openItem( void ); - void arrangeAndSet(BOOL set_selection, BOOL take_keyboard_focus); + void arrangeAndSet(bool set_selection, bool take_keyboard_focus); virtual ~LLFolderViewItem( void ); - // addToFolder() returns TRUE if it succeeds. FALSE otherwise + // addToFolder() returns true if it succeeds. false otherwise virtual void addToFolder(LLFolderViewFolder* folder); // Finds width and height of this object and it's children. Also @@ -184,13 +184,13 @@ public: S32 getTextPad(); // If 'selection' is 'this' then note that otherwise ignore. - // Returns TRUE if this item ends up being selected. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); + // Returns true if this item ends up being selected. + virtual bool setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus); // This method is used to set the selection state of an item. // If 'selection' is 'this' then note selection. - // Returns TRUE if the selection state of this item was changed. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); + // Returns true if the selection state of this item was changed. + virtual bool changeSelection(LLFolderViewItem* selection, bool selected); // this method is used to deselect this element void deselectItem(); @@ -202,24 +202,24 @@ public: virtual std::set getSelectionList() const; // Returns true is this object and all of its children can be removed (deleted by user) - virtual BOOL isRemovable(); + virtual bool isRemovable(); // Returns true is this object and all of its children can be moved - virtual BOOL isMovable(); + virtual bool isMovable(); // destroys this item recursively virtual void destroyView(); - BOOL isSelected() const { return mIsSelected; } + bool isSelected() const { return mIsSelected; } bool isInSelection() const; - void setUnselected() { mIsSelected = FALSE; } + void setUnselected() { mIsSelected = false; } - void setIsCurSelection(BOOL select) { mIsCurSelection = select; } + void setIsCurSelection(bool select) { mIsCurSelection = select; } - BOOL getIsCurSelection() const { return mIsCurSelection; } + bool getIsCurSelection() const { return mIsCurSelection; } - BOOL hasVisibleChildren() const { return mHasVisibleChildren; } + bool hasVisibleChildren() const { return mHasVisibleChildren; } // true if object can't have children virtual bool isFolderComplete() { return true; } @@ -229,8 +229,8 @@ public: // 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); - BOOL remove(); + //virtual bool removeRecursively(bool single_item); + bool remove(); // Build an appropriate context menu for the item. Flags unused. void buildContextMenu(class LLMenuGL& menu, U32 flags); @@ -249,8 +249,8 @@ public: void setParentFolder(LLFolderViewFolder* parent) { mParentFolder = parent; } - LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE ); - LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE ); + LLFolderViewItem* getNextOpenNode( bool include_children = true ); + LLFolderViewItem* getPreviousOpenNode( bool include_children = true ); const LLFolderViewModelItem* getViewModelItem( void ) const { return mViewModelItem; } LLFolderViewModelItem* getViewModelItem( void ) { return mViewModelItem; } @@ -262,16 +262,16 @@ public: void rename(const std::string& new_name); // Show children - virtual void setOpen(BOOL open = TRUE) {}; - virtual BOOL isOpen() const { return FALSE; } + virtual void setOpen(bool open = true) {}; + virtual bool isOpen() const { return false; } virtual LLFolderView* getRoot(); virtual const LLFolderView* getRoot() const; - BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); + bool isDescendantOf( const LLFolderViewFolder* potential_ancestor ); S32 getIndentation() const { return mIndentation; } - virtual BOOL passedFilter(S32 filter_generation = -1); - virtual BOOL isPotentiallyVisible(S32 filter_generation = -1); + virtual bool passedFilter(S32 filter_generation = -1); + virtual bool isPotentiallyVisible(S32 filter_generation = -1); // refresh information from the object being viewed. // refreshes label, suffixes and sets icons. Expensive! @@ -292,14 +292,14 @@ public: virtual void onMouseLeave(S32 x, S32 y, MASK mask); - //virtual LLView* findChildView(const std::string& name, BOOL recurse) const { return LLView::findChildView(name, recurse); } + //virtual LLView* findChildView(const std::string& name, bool recurse) const { return LLView::findChildView(name, recurse); } // virtual void handleDropped(); virtual void draw(); void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color); - void drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor); + void drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor); void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -324,7 +324,7 @@ protected: friend class LLUICtrlFactory; void updateLabelRotation(); - virtual bool isCollapsed() { return FALSE; } + virtual bool isCollapsed() { return false; } public: typedef std::list items_t; @@ -334,8 +334,8 @@ protected: items_t mItems; folders_t mFolders; - BOOL mIsOpen; - BOOL mExpanderHighlighted; + bool mIsOpen; + bool mExpanderHighlighted; F32 mCurHeight; F32 mTargetHeight; F32 mAutoOpenCountdown; @@ -356,40 +356,40 @@ public: virtual ~LLFolderViewFolder( void ); - LLFolderViewItem* getNextFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); - LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); + LLFolderViewItem* getNextFromChild( LLFolderViewItem*, bool include_children = true ); + LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, bool include_children = true ); - // addToFolder() returns TRUE if it succeeds. FALSE otherwise + // addToFolder() returns true if it succeeds. false otherwise virtual void addToFolder(LLFolderViewFolder* folder); // 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. virtual S32 arrange( S32* width, S32* height ); - BOOL needsArrange(); + bool needsArrange(); bool descendantsPassedFilter(S32 filter_generation = -1); // Passes selection information on to children and record // selection information if necessary. - // Returns TRUE if this object (or a child) ends up being selected. - // If 'openitem' is TRUE then folders are opened up along the way to the selection. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus = TRUE); + // Returns true if this object (or a child) ends up being selected. + // If 'openitem' is true then folders are opened up along the way to the selection. + virtual bool setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus = true); // This method is used to change the selection of an item. // Recursively traverse all children; if 'selection' is 'this' then change // the select status if necessary. - // Returns TRUE if the selection state of this folder, or of a child, was changed. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); + // Returns true if the selection state of this folder, or of a child, was changed. + virtual bool changeSelection(LLFolderViewItem* selection, bool selected); // this method is used to group select items void extendSelectionTo(LLFolderViewItem* selection); // Returns true is this object and all of its children can be removed. - virtual BOOL isRemovable(); + virtual bool isRemovable(); // Returns true is this object and all of its children can be moved - virtual BOOL isMovable(); + virtual bool isMovable(); // destroys this folder, and all children virtual void destroyView(); @@ -416,7 +416,7 @@ public: virtual void toggleOpen(); // Force a folder open or closed - virtual void setOpen(BOOL openitem = TRUE); + virtual void setOpen(bool openitem = true); // Called when a child is refreshed. virtual void requestArrange(); @@ -425,14 +425,14 @@ public: // method was written because the list iterators destroy the state // of other iterations, thus, we can't arrange while iterating // through the children (such as when setting which is selected. - virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse = RECURSE_NO); + virtual void setOpenArrangeRecursively(bool openitem, ERecurseType recurse = RECURSE_NO); // Get the current state of the folder. - virtual BOOL isOpen() const { return mIsOpen; } + virtual bool isOpen() const { return mIsOpen; } // special case if an object is dropped on the child. - BOOL handleDragAndDropFromChild(MASK mask, - BOOL drop, + bool handleDragAndDropFromChild(MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -451,14 +451,14 @@ public: virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask ); 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, + virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); - BOOL handleDragAndDropToThisFolder(MASK mask, - BOOL drop, + bool handleDragAndDropToThisFolder(MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index b3e3bc75e2..a91f202b2b 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -162,24 +162,24 @@ public: virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0; - virtual BOOL isItemWearable() const { return FALSE; } + virtual bool isItemWearable() const { return false; } - virtual BOOL isItemRenameable() const = 0; + virtual bool isItemRenameable() const = 0; virtual bool renameItem(const std::string& new_name) = 0; - virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder + virtual bool isItemMovable( void ) const = 0; // Can be moved to another folder virtual void move( LLFolderViewModelItem* parent_listener ) = 0; - virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed - virtual BOOL removeItem() = 0; + virtual bool isItemRemovable( void ) const = 0; // Can be destroyed + virtual bool removeItem() = 0; virtual void removeBatch(std::vector& batch) = 0; virtual bool isItemCopyable(bool can_copy_as_link = true) const = 0; - virtual BOOL copyToClipboard() const = 0; - virtual BOOL cutToClipboard() = 0; + virtual bool copyToClipboard() const = 0; + virtual bool cutToClipboard() = 0; virtual bool isCutToClipboard() { return false; }; - virtual BOOL isClipboardPasteable() const = 0; + virtual bool isClipboardPasteable() const = 0; virtual void pasteFromClipboard() = 0; virtual void pasteLinkFromClipboard() = 0; @@ -207,10 +207,10 @@ public: virtual void clearChildren() = 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, - // otherwise FALSE. - virtual BOOL dragOrDrop(MASK mask, BOOL drop, + // performed, and will set drop to true if a drop is + // requested. Returns true if a drop is possible/happened, + // otherwise false. + virtual bool dragOrDrop(MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, std::string& tooltip_msg) = 0; diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index c774a1105f..0738900f8c 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -96,7 +96,7 @@ bool LLIconCtrl::handleHover(S32 x, S32 y, MASK mask) return LLUICtrl::handleHover(x, y, mask); } -void LLIconCtrl::onVisibilityChange(BOOL new_visibility) +void LLIconCtrl::onVisibilityChange(bool new_visibility) { LLUICtrl::onVisibilityChange(new_visibility); if (mPriority == LLGLTexture::BOOST_ICON) diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index 76b24cae52..416de0cc9f 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -74,7 +74,7 @@ public: virtual bool handleHover(S32 x, S32 y, MASK mask); // lluictrl overrides - void onVisibilityChange(BOOL new_visibility); + void onVisibilityChange(bool new_visibility); virtual void setValue(const LLSD& value ); std::string getImageName() const; diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 69e338ddb9..36401f5542 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -525,7 +525,7 @@ void LLKeywords::findSegments(std::vector* seg_list, const LLW // Line start tokens { - BOOL line_done = FALSE; + bool line_done = false; for (token_list_t::iterator iter = mLineTokenList.begin(); iter != mLineTokenList.end(); ++iter) { @@ -542,7 +542,7 @@ void LLKeywords::findSegments(std::vector* seg_list, const LLW //create segments from seg_start to seg_end insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, defaultColor, editor); - line_done = TRUE; // to break out of second loop. + line_done = true; // to break out of second loop. break; } } diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 7e4e828a88..2db0101769 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -61,7 +61,7 @@ LLLayoutPanel::LLLayoutPanel(const Params& p) mMinDim(p.min_dim), mAutoResize(p.auto_resize), mUserResize(p.user_resize), - mCollapsed(FALSE), + mCollapsed(false), mCollapseAmt(0.f), mVisibleAmt(1.f), // default to fully visible mResizeBar(NULL), @@ -144,8 +144,8 @@ void LLLayoutPanel::setOrientation( LLView::EOrientation orientation ) ? getRect().getWidth() : getRect().getHeight())); - if (mAutoResize == FALSE - && mUserResize == TRUE + if (mAutoResize == false + && mUserResize == true && mMinDim == -1 ) { setMinDim(layout_dim); @@ -153,7 +153,7 @@ void LLLayoutPanel::setOrientation( LLView::EOrientation orientation ) mTargetDim = llmax(layout_dim, getMinDim()); } -void LLLayoutPanel::setVisible( BOOL visible ) +void LLLayoutPanel::setVisible( bool visible ) { if (visible != getVisible()) { @@ -166,7 +166,7 @@ void LLLayoutPanel::setVisible( BOOL visible ) LLPanel::setVisible(visible); } -void LLLayoutPanel::reshape( S32 width, S32 height, BOOL called_from_parent /*= TRUE*/ ) +void LLLayoutPanel::reshape( S32 width, S32 height, bool called_from_parent /*= true*/ ) { if (width == getRect().getWidth() && height == getRect().getHeight() && !LLView::sForceReshape) return; @@ -314,10 +314,10 @@ void LLLayoutStack::removeChild(LLView* view) } } -BOOL LLLayoutStack::postBuild() +bool LLLayoutStack::postBuild() { updateLayout(); - return TRUE; + return true; } bool LLLayoutStack::addChild(LLView* child, S32 tab_group) @@ -330,7 +330,7 @@ bool LLLayoutStack::addChild(LLView* child, S32 tab_group) createResizeBar(panelp); mNeedsLayout = true; } - BOOL result = LLView::addChild(child, tab_group); + bool result = LLView::addChild(child, tab_group); updateFractionalSizes(); return result; @@ -344,11 +344,11 @@ void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate) if (animate == ANIMATE) { panel->mVisibleAmt = 0.f; - panel->setVisible(TRUE); + panel->setVisible(true); } } -void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed) +void LLLayoutStack::collapsePanel(LLPanel* panel, bool collapsed) { LLLayoutPanel* panel_container = findEmbeddedPanel(panel); if (!panel_container) return; @@ -613,7 +613,7 @@ void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) border_params.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark"); addBorder(border_params); - setBorderVisible(TRUE); + setBorderVisible(true); LLImagePanel::Params image_panel; mDragHandleImage = LLUI::getUIImage(LLResizeBar::RIGHT == mSide ? "Vertical Drag Handle" : "Horizontal Drag Handle"); @@ -626,7 +626,7 @@ void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) //if (mShowDragHandle) //{ - // setBackgroundVisible(TRUE); + // setBackgroundVisible(true); // setTransparentColor(LLUIColorTable::instance().getColor("ResizebarBody")); //} @@ -982,7 +982,7 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& //normalizeFractionalSizes(); } -void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLLayoutStack::reshape(S32 width, S32 height, bool called_from_parent) { mNeedsLayout = true; LLView::reshape(width, height, called_from_parent); @@ -995,7 +995,7 @@ void LLLayoutStack::updateResizeBarLimits() { if (!visible_panelp->getVisible() || visible_panelp->mCollapsed) { - visible_panelp->mResizeBar->setVisible(FALSE); + visible_panelp->mResizeBar->setVisible(false); continue; } @@ -1005,14 +1005,14 @@ void LLLayoutStack::updateResizeBarLimits() && (visible_panelp->mAutoResize || visible_panelp->mUserResize) // current panel is resizable && (previous_visible_panelp->mAutoResize || previous_visible_panelp->mUserResize)) // previous panel is resizable { - visible_panelp->mResizeBar->setVisible(TRUE); + visible_panelp->mResizeBar->setVisible(true); S32 previous_panel_headroom = previous_visible_panelp->getVisibleDim() - previous_visible_panelp->getRelevantMinDim(); visible_panelp->mResizeBar->setResizeLimits(visible_panelp->getRelevantMinDim(), visible_panelp->getVisibleDim() + previous_panel_headroom); } else { - visible_panelp->mResizeBar->setVisible(FALSE); + visible_panelp->mResizeBar->setVisible(false); } previous_visible_panelp = visible_panelp; diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 000b919ae7..e3f8629425 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -71,9 +71,9 @@ public: /*virtual*/ void draw(); /*virtual*/ void deleteAllChildren(); /*virtual*/ void removeChild(LLView*); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); /*virtual*/ bool addChild(LLView* child, S32 tab_group = 0); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL); @@ -85,7 +85,7 @@ public: } EAnimate; void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE); - void collapsePanel(LLPanel* panel, BOOL collapsed = TRUE); + void collapsePanel(LLPanel* panel, bool collapsed = true); S32 getNumPanels() { return mPanels.size(); } void updateLayout(); @@ -156,10 +156,10 @@ public: void handleReshape(const LLRect& new_rect, bool by_user); - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + void reshape(S32 width, S32 height, bool called_from_parent = true); - void setVisible(BOOL visible); + void setVisible(bool visible); S32 getLayoutDim() const; S32 getTargetDim() const; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 9b320fdf97..6664117bcd 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -74,7 +74,7 @@ static LLDefaultChildRegistry::Register r1("line_editor"); // Compiler optimization, generate extern template template class LLLineEditor* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; // // Member functions @@ -127,10 +127,10 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mTextLeftEdge(0), // computed in updateTextPadding() below mTextRightEdge(0), // computed in updateTextPadding() below mCommitOnFocusLost( p.commit_on_focus_lost ), - mKeystrokeOnEsc(FALSE), + mKeystrokeOnEsc(false), mRevertOnEsc( p.revert_on_esc ), mKeystrokeCallback( p.keystroke_callback() ), - mIsSelecting( FALSE ), + mIsSelecting( false ), mSelectionStart( 0 ), mSelectionEnd( 0 ), mLastSelectionX(-1), @@ -138,23 +138,23 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mLastSelectionStart(-1), mLastSelectionEnd(-1), mBorderThickness( 0 ), - mIgnoreArrowKeys( FALSE ), + mIgnoreArrowKeys( false ), mIgnoreTab( p.ignore_tab ), mDrawAsterixes( p.is_password ), mSpellCheck( p.spellcheck ), mSpellCheckStart(-1), mSpellCheckEnd(-1), mSelectAllonFocusReceived( p.select_on_focus ), - mSelectAllonCommit( TRUE ), - mPassDelete(FALSE), - mReadOnly(FALSE), + mSelectAllonCommit( true ), + mPassDelete(false), + mReadOnly(false), mBgImage( p.background_image ), mBgImageDisabled( p.background_image_disabled ), mBgImageFocused( p.background_image_focused ), mShowImageFocused( p.bg_image_always_focused ), mUseBgColor(p.use_bg_color), - mHaveHistory(FALSE), - mReplaceNewlinesWithSpaces( TRUE ), + mHaveHistory(false), + mReplaceNewlinesWithSpaces( true ), mLabel(p.label), mCursorColor(p.cursor_color()), mBgColor(p.bg_color()), @@ -169,7 +169,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) { llassert( mMaxLengthBytes > 0 ); - LLUICtrl::setEnabled(TRUE); + LLUICtrl::setEnabled(true); setEnabled(p.enabled); mScrollTimer.reset(); @@ -214,7 +214,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) LLLineEditor::~LLLineEditor() { - mCommitOnFocusLost = FALSE; + mCommitOnFocusLost = false; // Make sure no context menu linger around once the widget is deleted LLContextMenu* menu = static_cast(mContextMenuHandle.get()); @@ -231,7 +231,7 @@ LLLineEditor::~LLLineEditor() void LLLineEditor::initFromParams(const LLLineEditor::Params& params) { LLUICtrl::initFromParams(params); - LLUICtrl::setEnabled(TRUE); + LLUICtrl::setEnabled(true); setEnabled(params.enabled); } @@ -280,9 +280,9 @@ void LLLineEditor::onCommit() if (mSelectAllonCommit) selectAll(); } -// Returns TRUE if user changed value at all +// Returns true if user changed value at all // virtual -BOOL LLLineEditor::isDirty() const +bool LLLineEditor::isDirty() const { return mText.getString() != mPrevText; } @@ -343,14 +343,14 @@ void LLLineEditor::updateHistory() } } -void LLLineEditor::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent) { LLUICtrl::reshape(width, height, called_from_parent); updateTextPadding(); // For clamping side-effect. setCursor(mCursorPos); // For clamping side-effect. } -void LLLineEditor::setEnabled(BOOL enabled) +void LLLineEditor::setEnabled(bool enabled) { mReadOnly = !enabled; setTabStop(!mReadOnly); @@ -405,7 +405,7 @@ void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit // Check to see if entire field is selected. S32 len = mText.length(); - BOOL all_selected = (len > 0) + bool all_selected = (len > 0) && (( mSelectionStart == 0 && mSelectionEnd == len ) || ( mSelectionStart == len && mSelectionEnd == 0 )); @@ -517,7 +517,7 @@ void LLLineEditor::resetScrollPosition() setCursor(getCursor()); } -BOOL LLLineEditor::canDeselect() const +bool LLLineEditor::canDeselect() const { return hasSelection(); } @@ -526,13 +526,13 @@ void LLLineEditor::deselect() { mSelectionStart = 0; mSelectionEnd = 0; - mIsSelecting = FALSE; + mIsSelecting = false; } void LLLineEditor::startSelection() { - mIsSelecting = TRUE; + mIsSelecting = true; mSelectionStart = getCursor(); mSelectionEnd = getCursor(); } @@ -541,14 +541,14 @@ void LLLineEditor::endSelection() { if( mIsSelecting ) { - mIsSelecting = FALSE; + mIsSelecting = false; mSelectionEnd = getCursor(); } } -BOOL LLLineEditor::canSelectAll() const +bool LLLineEditor::canSelectAll() const { - return TRUE; + return true; } void LLLineEditor::selectAll() @@ -562,7 +562,7 @@ void LLLineEditor::selectAll() mSelectionEnd = 0; setCursor(mSelectionEnd); //mScrollHPos = 0; - mIsSelecting = TRUE; + mIsSelecting = true; updatePrimary(); } @@ -976,19 +976,19 @@ void LLLineEditor::addChar(const llwchar uni_char) S32 new_bytes = wchar_utf8_length(new_c); - BOOL allow_char = TRUE; + bool allow_char = true; // Check byte length limit if ((new_bytes + cur_bytes) > mMaxLengthBytes) { - allow_char = FALSE; + allow_char = false; } else if (mMaxLengthChars) { S32 wide_chars = mText.getWString().size(); if ((wide_chars + 1) > mMaxLengthChars) { - allow_char = FALSE; + allow_char = false; } } @@ -1033,7 +1033,7 @@ void LLLineEditor::setSelection(S32 start, S32 end) { S32 len = mText.length(); - mIsSelecting = TRUE; + mIsSelecting = true; // JC, yes, this seems odd, but I think you have to presume a // selection dragged from the end towards the start. @@ -1042,7 +1042,7 @@ void LLLineEditor::setSelection(S32 start, S32 end) setCursor(start); } -void LLLineEditor::setDrawAsterixes(BOOL b) +void LLLineEditor::setDrawAsterixes(bool b) { mDrawAsterixes = b; updateAllowingLanguageInput(); @@ -1077,13 +1077,13 @@ S32 LLLineEditor::nextWordPos(S32 cursorPos) const } -BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask) +bool LLLineEditor::handleSelectionKey(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( mask & MASK_SHIFT ) { - handled = TRUE; + handled = true; switch( key ) { @@ -1136,7 +1136,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask) } default: - handled = FALSE; + handled = false; break; } } @@ -1167,7 +1167,7 @@ void LLLineEditor::deleteSelection() } } -BOOL LLLineEditor::canCut() const +bool LLLineEditor::canCut() const { return !mReadOnly && !mDrawAsterixes && hasSelection(); } @@ -1191,7 +1191,7 @@ void LLLineEditor::cut() deleteSelection(); // Validate new string and rollback the if needed. - BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); + bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); if( need_to_rollback ) { rollback.doRollback( this ); @@ -1204,7 +1204,7 @@ void LLLineEditor::cut() } } -BOOL LLLineEditor::canCopy() const +bool LLLineEditor::canCopy() const { return !mDrawAsterixes && hasSelection(); } @@ -1221,7 +1221,7 @@ void LLLineEditor::copy() } } -BOOL LLLineEditor::canPaste() const +bool LLLineEditor::canPaste() const { return !mReadOnly && LLClipboard::instance().isTextAvailable(); } @@ -1319,7 +1319,7 @@ void LLLineEditor::pasteHelper(bool is_primary) deselect(); // Validate new string and rollback the if needed. - BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); + bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); if( need_to_rollback ) { rollback.doRollback( this ); @@ -1344,7 +1344,7 @@ void LLLineEditor::copyPrimary() } } -BOOL LLLineEditor::canPastePrimary() const +bool LLLineEditor::canPastePrimary() const { return !mReadOnly && LLClipboard::instance().isTextAvailable(true); } @@ -1357,9 +1357,9 @@ void LLLineEditor::updatePrimary() } } -BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) +bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; switch( key ) { @@ -1369,7 +1369,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) gKeyboard->toggleInsertMode(); } - handled = TRUE; + handled = true; break; case KEY_BACKSPACE: @@ -1390,7 +1390,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) LLUI::getInstance()->reportBadKeystroke(); } } - handled = TRUE; + handled = true; break; case KEY_PAGE_UP: @@ -1398,7 +1398,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) if (!mIgnoreArrowKeys) { setCursor(0); - handled = TRUE; + handled = true; } break; @@ -1411,7 +1411,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { setCursor(len); } - handled = TRUE; + handled = true; } break; @@ -1438,7 +1438,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1465,7 +1465,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1482,7 +1482,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1499,7 +1499,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1528,10 +1528,10 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) } -BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask ) +bool LLLineEditor::handleKeyHere(KEY key, MASK mask ) { - BOOL handled = FALSE; - BOOL selection_modified = FALSE; + bool handled = false; + bool selection_modified = false; if ( gFocusMgr.getKeyboardFocus() == this ) { @@ -1566,7 +1566,7 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask ) deselect(); } - BOOL need_to_rollback = FALSE; + bool need_to_rollback = false; // If read-only, don't allow changes need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText())); @@ -1625,7 +1625,7 @@ bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) deselect(); - BOOL need_to_rollback = FALSE; + bool need_to_rollback = false; // Validate new string and rollback the keystroke if needed. need_to_rollback |= ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); @@ -1651,7 +1651,7 @@ bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) } -BOOL LLLineEditor::canDoDelete() const +bool LLLineEditor::canDoDelete() const { return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) ); } @@ -1681,7 +1681,7 @@ void LLLineEditor::doDelete() } // Validate new string and rollback the if needed. - BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); + bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); if( need_to_rollback ) { rollback.doRollback( this ); @@ -1702,7 +1702,7 @@ void LLLineEditor::drawBackground() F32 alpha = getCurrentTransparency(); if (mUseBgColor) { - gl_rect_2d(getLocalRect(), mBgColor % alpha, TRUE); + gl_rect_2d(getLocalRect(), mBgColor % alpha, true); } else { @@ -1923,7 +1923,7 @@ void LLLineEditor::draw() &rendered_pixels_right); } #if 1 // for when we're ready for image art. - mBorder->setVisible(FALSE); // no more programmatic art. + mBorder->setVisible(false); // no more programmatic art. #endif if ( (getSpellCheck()) && (mText.length() > 2) ) @@ -2024,7 +2024,7 @@ void LLLineEditor::draw() // If we're editing... if( hasFocus()) { - //mBorder->setVisible(TRUE); // ok, programmer art just this once. + //mBorder->setVisible(true); // ok, programmer art just this once. // (Flash the cursor every half second) if (!mReadOnly && gFocusMgr.getAppHasFocus()) { @@ -2080,16 +2080,16 @@ void LLLineEditor::draw() LLFontGL::NO_SHADOW, S32_MAX, mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right, FALSE); + &rendered_pixels_right, false); } // Draw children (border) - //mBorder->setVisible(TRUE); - mBorder->setKeyboardFocusHighlight( TRUE ); + //mBorder->setVisible(true); + mBorder->setKeyboardFocusHighlight( true ); LLView::draw(); - mBorder->setKeyboardFocusHighlight( FALSE ); - //mBorder->setVisible(FALSE); + mBorder->setKeyboardFocusHighlight( false ); + //mBorder->setVisible(false); } else // does not have keyboard input { @@ -2105,7 +2105,7 @@ void LLLineEditor::draw() LLFontGL::NO_SHADOW, S32_MAX, mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right, FALSE); + &rendered_pixels_right, false); } // Draw children (border) LLView::draw(); @@ -2162,19 +2162,19 @@ void LLLineEditor::onTabInto() } //virtual -BOOL LLLineEditor::acceptsTextInput() const +bool LLLineEditor::acceptsTextInput() const { - return TRUE; + return true; } // Start or stop the editor from accepting text-editing keystrokes -void LLLineEditor::setFocus( BOOL new_state ) +void LLLineEditor::setFocus( bool new_state ) { - BOOL old_state = hasFocus(); + bool old_state = hasFocus(); if (!new_state) { - getWindow()->allowLanguageTextInput(this, FALSE); + getWindow()->allowLanguageTextInput(this, false); } @@ -2185,7 +2185,7 @@ void LLLineEditor::setFocus( BOOL new_state ) // We don't want handleMouseUp() to "finish" the selection (and thereby // set mSelectionEnd to where the mouse is), so we finish the selection // here. - mIsSelecting = FALSE; + mIsSelecting = false; } if( new_state ) @@ -2258,13 +2258,13 @@ bool LLLineEditor::prevalidateInput(const LLWString& wstr) } // static -BOOL LLLineEditor::postvalidateFloat(const std::string &str) +bool LLLineEditor::postvalidateFloat(const std::string &str) { LLLocale locale(LLLocale::USER_LOCALE); - BOOL success = TRUE; - BOOL has_decimal = FALSE; - BOOL has_digit = FALSE; + bool success = true; + bool has_decimal = false; + bool has_digit = false; LLWString trimmed = utf8str_to_wstring(str); LLWStringUtil::trim(trimmed); @@ -2289,22 +2289,22 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str) if( has_decimal ) { // can't have two - success = FALSE; + success = false; break; } else { - has_decimal = TRUE; + has_decimal = true; } } else if( LLStringOps::isDigit( trimmed[i] ) ) { - has_digit = TRUE; + has_digit = true; } else { - success = FALSE; + success = false; break; } } @@ -2316,7 +2316,7 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str) return success; } -BOOL LLLineEditor::evaluateFloat() +bool LLLineEditor::evaluateFloat() { bool success; F32 result = 0.f; @@ -2348,7 +2348,7 @@ void LLLineEditor::onMouseCaptureLost() } -void LLLineEditor::setSelectAllonFocusReceived(BOOL b) +void LLLineEditor::setSelectAllonFocusReceived(bool b) { mSelectAllonFocusReceived = b; } @@ -2369,16 +2369,16 @@ void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data) } -BOOL LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) +bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) { mText.setArg(key, text); - return TRUE; + return true; } -BOOL LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) { mLabel.setArg(key, text); - return TRUE; + return true; } @@ -2398,15 +2398,15 @@ void LLLineEditor::updateAllowingLanguageInput() } if (hasFocus() && !mReadOnly && !mDrawAsterixes && mPrevalidateFunc == NULL) { - window->allowLanguageTextInput(this, TRUE); + window->allowLanguageTextInput(this, true); } else { - window->allowLanguageTextInput(this, FALSE); + window->allowLanguageTextInput(this, false); } } -BOOL LLLineEditor::hasPreeditString() const +bool LLLineEditor::hasPreeditString() const { return (mPreeditPositions.size() > 1); } @@ -2612,7 +2612,7 @@ S32 LLLineEditor::getPreeditFontSize() const return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); } -void LLLineEditor::setReplaceNewlinesWithSpaces(BOOL replace) +void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace) { mReplaceNewlinesWithSpaces = replace; } diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index cdb514deaa..1ca300ec91 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -128,31 +128,31 @@ public: /*virtual*/ bool handleDoubleClick(S32 x,S32 y,MASK mask); /*virtual*/ bool handleMiddleMouseDown(S32 x,S32 y,MASK mask); /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask ); /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); /*virtual*/ void onMouseCaptureLost(); // LLEditMenuHandler overrides virtual void cut(); - virtual BOOL canCut() const; + virtual bool canCut() const; virtual void copy(); - virtual BOOL canCopy() const; + virtual bool canCopy() const; virtual void paste(); - virtual BOOL canPaste() const; + virtual bool canPaste() const; virtual void updatePrimary(); virtual void copyPrimary(); virtual void pastePrimary(); - virtual BOOL canPastePrimary() const; + virtual bool canPastePrimary() const; virtual void doDelete(); - virtual BOOL canDoDelete() const; + virtual bool canDoDelete() const; virtual void selectAll(); - virtual BOOL canSelectAll() const; + virtual bool canSelectAll() const; virtual void deselect(); - virtual BOOL canDeselect() const; + virtual bool canDeselect() const; // LLSpellCheckMenuHandler overrides /*virtual*/ bool getSpellCheck() const; @@ -174,26 +174,26 @@ public: // view overrides virtual void draw(); - virtual void reshape(S32 width,S32 height,BOOL called_from_parent=TRUE); + virtual void reshape(S32 width,S32 height,bool called_from_parent=true); virtual void onFocusReceived(); virtual void onFocusLost(); - virtual void setEnabled(BOOL enabled); + virtual void setEnabled(bool enabled); // UI control overrides virtual void clear(); virtual void onTabInto(); - virtual void setFocus( BOOL b ); + virtual void setFocus( bool b ); virtual void setRect(const LLRect& rect); - virtual BOOL acceptsTextInput() const; + virtual bool acceptsTextInput() const; virtual void onCommit(); - virtual BOOL isDirty() const; // Returns TRUE if user changed value at all + virtual bool isDirty() const; // Returns true if user changed value at all virtual void resetDirty(); // Clear dirty state // assumes UTF8 text virtual void setValue(const LLSD& value ); virtual LLSD getValue() const; - virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text ); - virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setTextArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; } const std::string& getLabel() { return mLabel.getString(); } @@ -217,9 +217,9 @@ public: void setSelection(S32 start, S32 end); virtual void getSelectionRange(S32 *position, S32 *length) const; - void setCommitOnFocusLost( BOOL b ) { mCommitOnFocusLost = b; } - void setRevertOnEsc( BOOL b ) { mRevertOnEsc = b; } - void setKeystrokeOnEsc(BOOL b) { mKeystrokeOnEsc = b; } + void setCommitOnFocusLost( bool b ) { mCommitOnFocusLost = b; } + void setRevertOnEsc( bool b ) { mRevertOnEsc = b; } + void setKeystrokeOnEsc(bool b) { mKeystrokeOnEsc = b; } void setCursorColor(const LLColor4& c) { mCursorColor = c; } const LLColor4& getCursorColor() const { return mCursorColor.get(); } @@ -235,23 +235,23 @@ public: const LLFontGL* getFont() const { return mGLFont; } void setFont(const LLFontGL* font); - void setIgnoreArrowKeys(BOOL b) { mIgnoreArrowKeys = b; } - void setIgnoreTab(BOOL b) { mIgnoreTab = b; } - void setPassDelete(BOOL b) { mPassDelete = b; } - void setDrawAsterixes(BOOL b); + void setIgnoreArrowKeys(bool b) { mIgnoreArrowKeys = b; } + void setIgnoreTab(bool b) { mIgnoreTab = b; } + void setPassDelete(bool b) { mPassDelete = b; } + void setDrawAsterixes(bool b); // get the cursor position of the beginning/end of the prev/next word in the text S32 prevWordPos(S32 cursorPos) const; S32 nextWordPos(S32 cursorPos) const; - BOOL hasSelection() const { return (mSelectionStart != mSelectionEnd); } + bool hasSelection() const { return (mSelectionStart != mSelectionEnd); } void startSelection(); void endSelection(); void extendSelection(S32 new_cursor_pos); void deleteSelection(); - void setSelectAllonFocusReceived(BOOL b); - void setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; } + void setSelectAllonFocusReceived(bool b); + void setSelectAllonCommit(bool b) { mSelectAllonCommit = b; } void onKeystroke(); typedef boost::function callback_t; @@ -270,16 +270,16 @@ public: // Also callback that this method sets differs from setPrevalidate in a way that it validates just inputed // symbols, before existing text is modified, but setPrevalidate validates line after it was modified. void setPrevalidateInput(LLTextValidate::validate_func_t func); - static BOOL postvalidateFloat(const std::string &str); + static bool postvalidateFloat(const std::string &str); bool prevalidateInput(const LLWString& wstr); - BOOL evaluateFloat(); + bool evaluateFloat(); // line history support: - void setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off + void setEnableLineHistory( bool enabled ) { mHaveHistory = enabled; } // switches line history on or off void updateHistory(); // stores current line in history - void setReplaceNewlinesWithSpaces(BOOL replace); + void setReplaceNewlinesWithSpaces(bool replace); void resetContextMenu() { setContextMenu(NULL); }; @@ -299,9 +299,9 @@ public: void setCursorAtLocalPos(S32 local_mouse_x); S32 findPixelNearestPos(S32 cursor_offset = 0) const; S32 calcCursorPos(S32 mouse_x); - BOOL handleSpecialKey(KEY key, MASK mask); - BOOL handleSelectionKey(KEY key, MASK mask); - BOOL handleControlKey(KEY key, MASK mask); + bool handleSpecialKey(KEY key, MASK mask); + bool handleSelectionKey(KEY key, MASK mask); + bool handleControlKey(KEY key, MASK mask); S32 handleCommitKey(KEY key, MASK mask); void updateTextPadding(); @@ -312,7 +312,7 @@ public: // private data members // void updateAllowingLanguageInput(); - BOOL hasPreeditString() const; + bool hasPreeditString() const; // Implementation (overrides) of LLPreeditor virtual void resetPreedit(); virtual void updatePreedit(const LLWString &preedit_string, @@ -333,7 +333,7 @@ protected: LLUIString mLabel; // text label that is visible when no user text provided // line history support: - BOOL mHaveHistory; // flag for enabled line history + bool mHaveHistory; // flag for enabled line history typedef std::vector line_history_t; line_history_t mLineHistory; // line history storage line_history_t::iterator mCurrentHistoryLine; // currently browsed history line @@ -350,13 +350,13 @@ protected: S32 mTextLeftEdge; // Pixels, cached left edge of text based on left padding and width S32 mTextRightEdge; // Pixels, cached right edge of text based on right padding and width - BOOL mCommitOnFocusLost; - BOOL mRevertOnEsc; - BOOL mKeystrokeOnEsc; + bool mCommitOnFocusLost; + bool mRevertOnEsc; + bool mKeystrokeOnEsc; keystroke_callback_t mKeystrokeCallback; - BOOL mIsSelecting; // Selection for clipboard operations + bool mIsSelecting; // Selection for clipboard operations S32 mSelectionStart; S32 mSelectionEnd; S32 mLastSelectionX; @@ -387,17 +387,17 @@ protected: S32 mBorderThickness; - BOOL mIgnoreArrowKeys; - BOOL mIgnoreTab; - BOOL mDrawAsterixes; + bool mIgnoreArrowKeys; + bool mIgnoreTab; + bool mDrawAsterixes; - BOOL mSelectAllonFocusReceived; - BOOL mSelectAllonCommit; - BOOL mPassDelete; + bool mSelectAllonFocusReceived; + bool mSelectAllonCommit; + bool mPassDelete; - BOOL mReadOnly; + bool mReadOnly; - BOOL mShowImageFocused; + bool mShowImageFocused; bool mUseBgColor; @@ -416,7 +416,7 @@ private: LLPointer mBgImageDisabled; LLPointer mBgImageFocused; - BOOL mReplaceNewlinesWithSpaces; // if false, will replace pasted newlines with paragraph symbol. + bool mReplaceNewlinesWithSpaces; // if false, will replace pasted newlines with paragraph symbol. // private helper class class LLLineEditorRollback @@ -450,7 +450,7 @@ private: std::string mText; S32 mCursorPos; S32 mScrollHPos; - BOOL mIsSelecting; + bool mIsSelecting; S32 mSelectionStart; S32 mSelectionEnd; }; // end class LLLineEditorRollback @@ -460,7 +460,7 @@ private: // Build time optimization, generate once in .cpp file #ifndef LLLINEEDITOR_CPP extern template class LLLineEditor* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif #endif // LL_LINEEDITOR_ diff --git a/indra/llui/lllocalcliprect.cpp b/indra/llui/lllocalcliprect.cpp index f3a526faeb..45b7a0948a 100644 --- a/indra/llui/lllocalcliprect.cpp +++ b/indra/llui/lllocalcliprect.cpp @@ -32,7 +32,7 @@ /*static*/ std::stack LLScreenClipRect::sClipRectStack; -LLScreenClipRect::LLScreenClipRect(const LLRect& rect, BOOL enabled) +LLScreenClipRect::LLScreenClipRect(const LLRect& rect, bool enabled) : mScissorState(GL_SCISSOR_TEST), mEnabled(enabled) { @@ -99,7 +99,7 @@ void LLScreenClipRect::updateScissorRegion() //--------------------------------------------------------------------------- // LLLocalClipRect //--------------------------------------------------------------------------- -LLLocalClipRect::LLLocalClipRect(const LLRect& rect, BOOL enabled /* = TRUE */) +LLLocalClipRect::LLLocalClipRect(const LLRect& rect, bool enabled /* = true */) : LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX, rect.mTop + LLFontGL::sCurOrigin.mY, rect.mRight + LLFontGL::sCurOrigin.mX, diff --git a/indra/llui/lllocalcliprect.h b/indra/llui/lllocalcliprect.h index eeeaf2adb6..a258d34f31 100644 --- a/indra/llui/lllocalcliprect.h +++ b/indra/llui/lllocalcliprect.h @@ -38,7 +38,7 @@ class LLScreenClipRect { public: - LLScreenClipRect(const LLRect& rect, BOOL enabled = TRUE); + LLScreenClipRect(const LLRect& rect, bool enabled = true); virtual ~LLScreenClipRect(); private: @@ -48,7 +48,7 @@ private: private: LLGLState mScissorState; - BOOL mEnabled; + bool mEnabled; static std::stack sClipRectStack; }; @@ -56,7 +56,7 @@ private: class LLLocalClipRect : public LLScreenClipRect { public: - LLLocalClipRect(const LLRect& rect, BOOL enabled = TRUE); + LLLocalClipRect(const LLRect& rect, bool enabled = true); ~LLLocalClipRect(); }; diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index ceb78387ac..5374b7ea73 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -78,7 +78,7 @@ void LLMenuButton::hideMenu() LLToggleableMenu* menu = getMenu(); if (menu) { - menu->setVisible(FALSE); + menu->setVisible(false); } } @@ -118,9 +118,9 @@ void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_T menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); } -BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) +bool LLMenuButton::handleKeyHere(KEY key, MASK mask ) { - if (!getMenu()) return FALSE; + if (!getMenu()) return false; if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) { @@ -129,17 +129,17 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) LLUICtrl::handleMouseDown(-1, -1, MASK_NONE); toggleMenu(); - return TRUE; + return true; } LLToggleableMenu* menu = getMenu(); if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) { - menu->setVisible(FALSE); - return TRUE; + menu->setVisible(false); + return true; } - return FALSE; + return false; } bool LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index b6ec4e2e35..1854f459d5 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -66,7 +66,7 @@ public: boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask ); void hideMenu(); diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 755ff5f4e3..76da0755af 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -101,7 +101,7 @@ const std::string LLMenuGL::ARROW_DOWN("vvvvvvv"); const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f; -BOOL LLMenuGL::sKeyboardMode = FALSE; +bool LLMenuGL::sKeyboardMode = false; LLHandle LLMenuHolderGL::sItemLastSelectedHandle; LLFrameTimer LLMenuHolderGL::sItemActivationTimer; @@ -151,10 +151,10 @@ LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) : LLUICtrl(p), mJumpKey(p.jump_key), mAllowKeyRepeat(p.allow_key_repeat), - mHighlight( FALSE ), - mGotHover( FALSE ), - mBriefItem( FALSE ), - mDrawTextDisabled( FALSE ), + mHighlight( false ), + mGotHover( false ), + mBriefItem( false ), + mDrawTextDisabled( false ), mFont(p.font), mAcceleratorKey(KEY_NONE), mAcceleratorMask(MASK_NONE), @@ -166,7 +166,7 @@ LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) { #ifdef LL_DARWIN // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd - BOOL useMacCtrl = p.use_mac_ctrl; + bool useMacCtrl = p.use_mac_ctrl; #endif // LL_DARWIN std::string shortcut = p.shortcut; @@ -219,14 +219,14 @@ bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const } //virtual -BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) +bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) { if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) { onCommit(); - return TRUE; + return true; } - return FALSE; + return false; } bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) @@ -243,13 +243,13 @@ bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask) { - setHover(TRUE); + setHover(true); LLUICtrl::onMouseEnter(x,y,mask); } void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask) { - setHover(FALSE); + setHover(false); LLUICtrl::onMouseLeave(x,y,mask); } @@ -269,7 +269,7 @@ bool LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK 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) +bool LLMenuItemGL::addToAcceleratorList(std::list *listp) { LLMenuKeyboardBinding *accelerator = NULL; @@ -294,7 +294,7 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list *list // LL_WARNS() << warning << LL_ENDL; // LLAlertDialog::modalAlert(warning); - return FALSE; + return false; } } if (!accelerator) @@ -309,7 +309,7 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list *list listp->push_back(accelerator);//addData(accelerator); } } - return TRUE; + return true; } // This function appends the character string representation of @@ -333,13 +333,13 @@ U32 LLMenuItemGL::getNominalHeight( void ) const } //virtual -void LLMenuItemGL::setBriefItem(BOOL brief) +void LLMenuItemGL::setBriefItem(bool brief) { mBriefItem = brief; } //virtual -BOOL LLMenuItemGL::isBriefItem() const +bool LLMenuItemGL::isBriefItem() const { return mBriefItem; } @@ -403,7 +403,7 @@ void LLMenuItemGL::onCommit( void ) } // set the hover status (called by it's menu) - void LLMenuItemGL::setHighlight( BOOL highlight ) + void LLMenuItemGL::setHighlight( bool highlight ) { if (highlight) { @@ -419,7 +419,7 @@ void LLMenuItemGL::onCommit( void ) } -BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) +bool LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) { if (getHighlight() && getMenu()->isOpen()) @@ -427,30 +427,30 @@ BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) if (key == KEY_UP) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); getMenu()->highlightPrevItem(this); - return TRUE; + return true; } else if (key == KEY_DOWN) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); getMenu()->highlightNextItem(this); - return TRUE; + return true; } else if (key == KEY_RETURN && mask == MASK_NONE) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); onCommit(); - return TRUE; + return true; } } - return FALSE; + return false; } bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask) @@ -522,19 +522,19 @@ void LLMenuItemGL::draw( void ) if( !mDrawBoolLabel.empty() ) { mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); } mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); if( !mDrawAccelLabel.empty() ) { mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); } if( !mDrawBranchLabel.empty() ) { mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); } } @@ -553,13 +553,13 @@ void LLMenuItemGL::draw( void ) } } -BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text ) { mLabel.setArg(key, text); - return TRUE; + return true; } -void LLMenuItemGL::onVisibilityChange(BOOL new_visibility) +void LLMenuItemGL::onVisibilityChange(bool new_visibility) { if (getMenu()) { @@ -735,12 +735,12 @@ void LLMenuItemTearOffGL::onCommit() { if (parent_floater) { - parent_floater->addDependentFloater(tear_off_menu, FALSE); + parent_floater->addDependentFloater(tear_off_menu, false); } // give focus to torn off menu because it will have // been taken away when parent menu closes - tear_off_menu->setFocus(TRUE); + tear_off_menu->setFocus(true); } } LLMenuItemGL::onCommit(); @@ -860,12 +860,12 @@ void LLMenuItemCallGL::buildDrawLabel( void ) LLMenuItemGL::buildDrawLabel(); } -BOOL LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask ) +bool LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask ) { return LLMenuItemGL::handleKeyHere(key, mask); } -BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) +bool LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) { if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) { @@ -873,10 +873,10 @@ BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) if (getEnabled()) { onCommit(); - return TRUE; + return true; } } - return FALSE; + return false; } // handleRightMouseUp moved into base class LLMenuItemGL so clicks are @@ -968,7 +968,7 @@ LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p) if (branch) { mBranchHandle = branch->getHandle(); - branch->setVisible(FALSE); + branch->setVisible(false); branch->setParentMenuItem(this); } } @@ -984,7 +984,7 @@ LLMenuItemBranchGL::~LLMenuItemBranchGL() // virtual -LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const +LLView* LLMenuItemBranchGL::getChildView(const std::string& name, bool recurse) const { LLMenuGL* branch = getBranch(); if (branch) @@ -1001,7 +1001,7 @@ LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) return LLView::getChildView(name, recurse); } -LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) const +LLView* LLMenuItemBranchGL::findChildView(const std::string& name, bool recurse) const { LLMenuGL* branch = getBranch(); if (branch) @@ -1022,7 +1022,7 @@ LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) { // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(FALSE); + LLMenuGL::setKeyboardMode(false); onCommit(); make_ui_sound("UISndClickRelease"); @@ -1034,18 +1034,18 @@ bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const return getBranch() && getBranch()->hasAccelerator(key, mask); } -BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) +bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) { return getBranch() && getBranch()->handleAcceleratorKey(key, mask); } // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list -BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) +bool LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) { LLMenuGL* branch = getBranch(); if (!branch) - return FALSE; + return false; U32 item_count = branch->getItemCount(); LLMenuItemGL *item; @@ -1058,7 +1058,7 @@ BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list } } - return FALSE; + return false; } @@ -1086,9 +1086,9 @@ void LLMenuItemBranchGL::onCommit( void ) LLUICtrl::onCommit(); } -BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) +bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent) { - BOOL handled = FALSE; + bool handled = false; if (getBranch() && called_from_parent) { handled = getBranch()->handleKey(key, mask, called_from_parent); @@ -1102,12 +1102,12 @@ BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) return handled; } -BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, bool called_from_parent) { - BOOL handled = FALSE; + bool handled = false; if (getBranch() && called_from_parent) { - handled = getBranch()->handleUnicodeChar(uni_char, TRUE); + handled = getBranch()->handleUnicodeChar(uni_char, true); } if (!handled) @@ -1119,7 +1119,7 @@ BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_pa } -void LLMenuItemBranchGL::setHighlight( BOOL highlight ) +void LLMenuItemBranchGL::setHighlight( bool highlight ) { if (highlight == getHighlight()) return; @@ -1128,17 +1128,17 @@ void LLMenuItemBranchGL::setHighlight( BOOL highlight ) if (!branch) return; - BOOL auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff()); + bool auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff()); // torn off menus don't open sub menus on hover unless they have focus LLFloater * menu_parent = dynamic_cast(getMenu()->getParent()); if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus()) { - auto_open = FALSE; + auto_open = false; } // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus) if (branch->getTornOff()) { - auto_open = FALSE; + auto_open = false; } LLMenuItemGL::setHighlight(highlight); if( highlight ) @@ -1155,13 +1155,13 @@ void LLMenuItemBranchGL::setHighlight( BOOL highlight ) LLFloater * branch_parent = dynamic_cast(branch->getParent()); if (branch_parent) { - branch_parent->setFocus(FALSE); + branch_parent->setFocus(false); } branch->clearHoverItem(); } else { - branch->setVisible( FALSE ); + branch->setVisible( false ); } } } @@ -1171,7 +1171,7 @@ void LLMenuItemBranchGL::draw() LLMenuItemGL::draw(); if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff()) { - setHighlight(TRUE); + setHighlight(true); } } @@ -1184,16 +1184,16 @@ void LLMenuItemBranchGL::updateBranchParent(LLView* parentp) } } -void LLMenuItemBranchGL::onVisibilityChange( BOOL new_visibility ) +void LLMenuItemBranchGL::onVisibilityChange( bool new_visibility ) { - if (new_visibility == FALSE && getBranch() && !getBranch()->getTornOff()) + if (new_visibility == false && getBranch() && !getBranch()->getTornOff()) { - getBranch()->setVisible(FALSE); + getBranch()->setVisible(false); } LLMenuItemGL::onVisibilityChange(new_visibility); } -BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) +bool LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) { LLMenuGL* branch = getBranch(); if (!branch) @@ -1208,15 +1208,15 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) if (branch->getVisible() && key == KEY_LEFT) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); - BOOL handled = branch->clearHoverItem(); + bool handled = branch->clearHoverItem(); if (branch->getTornOff()) { LLFloater * branch_parent = dynamic_cast(branch->getParent()); if (branch_parent) { - branch_parent->setFocus(FALSE); + branch_parent->setFocus(false); } } if (handled && getMenu()->getTornOff()) @@ -1224,7 +1224,7 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) LLFloater * menu_parent = dynamic_cast(getMenu()->getParent()); if (menu_parent) { - menu_parent->setFocus(TRUE); + menu_parent->setFocus(true); } } return handled; @@ -1233,12 +1233,12 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) if (key == KEY_RIGHT && !branch->getHighlightedItem()) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); LLMenuItemGL* itemp = branch->highlightNextItem(NULL); if (itemp) { - return TRUE; + return true; } } } @@ -1246,13 +1246,13 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) } //virtual -BOOL LLMenuItemBranchGL::isActive() const +bool LLMenuItemBranchGL::isActive() const { return isOpen() && getBranch() && getBranch()->getHighlightedItem(); } //virtual -BOOL LLMenuItemBranchGL::isOpen() const +bool LLMenuItemBranchGL::isOpen() const { return getBranch() && getBranch()->isOpen(); } @@ -1323,7 +1323,7 @@ void LLMenuItemBranchGL::openMenu() } branch->translate( delta_x, delta_y ); - branch->setVisible( TRUE ); + branch->setVisible( true ); branch->getParent()->sendChildToFront(branch); dirtyRect(); @@ -1358,20 +1358,20 @@ public: // set the hover status (called by it's menu) and if the object is // active. This is used for behavior transfer. - virtual void setHighlight( BOOL highlight ); + virtual void setHighlight( bool highlight ); - virtual BOOL isActive( void ) const; + virtual bool isActive( void ) const; // LLView functionality virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); virtual void draw( void ); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); - virtual BOOL handleAcceleratorKey(KEY key, MASK mask); + virtual bool handleAcceleratorKey(KEY key, MASK mask); virtual void onFocusLost(); - virtual void setFocus(BOOL b); + virtual void setFocus(bool b); }; LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) : @@ -1403,7 +1403,7 @@ void LLMenuItemBranchDownGL::openMenu( void ) LLMenuGL* branch = getBranch(); if( branch->getVisible() && !branch->getTornOff() ) { - branch->setVisible( FALSE ); + branch->setVisible( false ); } else { @@ -1444,15 +1444,15 @@ void LLMenuItemBranchDownGL::openMenu( void ) } branch->translate( delta_x, 0 ); - setHighlight(TRUE); - branch->setVisible( TRUE ); + setHighlight(true); + branch->setVisible( true ); branch->getParent()->sendChildToFront(branch); } } } // set the hover status (called by it's menu) -void LLMenuItemBranchDownGL::setHighlight( BOOL highlight ) +void LLMenuItemBranchDownGL::setHighlight( bool highlight ) { if (highlight == getHighlight()) return; @@ -1471,18 +1471,18 @@ void LLMenuItemBranchDownGL::setHighlight( BOOL highlight ) LLFloater * branch_parent = dynamic_cast(branch->getParent()); if (branch_parent) { - branch_parent->setFocus(FALSE); + branch_parent->setFocus(false); } branch->clearHoverItem(); } else { - branch->setVisible( FALSE ); + branch->setVisible( false ); } } } -BOOL LLMenuItemBranchDownGL::isActive() const +bool LLMenuItemBranchDownGL::isActive() const { // for top level menus, being open is sufficient to be considered // active, because clicking on them with the mouse will open @@ -1514,10 +1514,10 @@ bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask ) } -BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) +bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) { - BOOL branch_visible = getBranch()->getVisible(); - BOOL handled = getBranch()->handleAcceleratorKey(key, mask); + bool branch_visible = getBranch()->getVisible(); + bool handled = getBranch()->handleAcceleratorKey(key, mask); if (handled && !branch_visible && isInVisibleChain()) { // flash this menu entry because we triggered an invisible menu item @@ -1530,11 +1530,11 @@ void LLMenuItemBranchDownGL::onFocusLost() { // needed for tab-based selection LLMenuItemBranchGL::onFocusLost(); - LLMenuGL::setKeyboardMode(FALSE); - setHighlight(FALSE); + LLMenuGL::setKeyboardMode(false); + setHighlight(false); } -void LLMenuItemBranchDownGL::setFocus(BOOL b) +void LLMenuItemBranchDownGL::setFocus(bool b) { // needed for tab-based selection LLMenuItemBranchGL::setFocus(b); @@ -1542,16 +1542,16 @@ void LLMenuItemBranchDownGL::setFocus(BOOL b) setHighlight(b); } -BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) +bool LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) { - BOOL menu_open = getBranch()->getVisible(); + bool menu_open = getBranch()->getVisible(); // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode())) { if (key == KEY_LEFT) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this); // open new menu only if previous menu was open @@ -1560,12 +1560,12 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) itemp->onCommit(); } - return TRUE; + return true; } else if (key == KEY_RIGHT) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); LLMenuItemGL* itemp = getMenu()->highlightNextItem(this); // open new menu only if previous menu was open @@ -1574,35 +1574,35 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) itemp->onCommit(); } - return TRUE; + return true; } else if (key == KEY_DOWN) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); if (!isActive()) { onCommit(); } getBranch()->highlightNextItem(NULL); - return TRUE; + return true; } else if (key == KEY_UP) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); if (!isActive()) { onCommit(); } getBranch()->highlightPrevItem(NULL); - return TRUE; + return true; } } - return FALSE; + return false; } void LLMenuItemBranchDownGL::draw( void ) @@ -1610,7 +1610,7 @@ void LLMenuItemBranchDownGL::draw( void ) //FIXME: try removing this if (getBranch()->getVisible() && !getBranch()->getTornOff()) { - setHighlight(TRUE); + setHighlight(true); } if( getHighlight() ) @@ -1682,8 +1682,8 @@ protected: public: /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); - /*virtual*/ void setEnabled(BOOL enabled); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent); + /*virtual*/ void setEnabled(bool enabled); virtual void onCommit( void ); private: @@ -1733,14 +1733,14 @@ void LLMenuScrollItem::draw() } /*virtual*/ -void LLMenuScrollItem::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLMenuScrollItem::reshape(S32 width, S32 height, bool called_from_parent) { mArrowBtn->reshape(width, height, called_from_parent); LLView::reshape(width, height, called_from_parent); } /*virtual*/ -void LLMenuScrollItem::setEnabled(BOOL enabled) +void LLMenuScrollItem::setEnabled(bool enabled) { mArrowBtn->setEnabled(enabled); LLView::setEnabled(enabled); @@ -1762,7 +1762,7 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) mDropShadowed( p.drop_shadow ), mHasSelection(false), mHorizontalLayout( p.horizontal_layout ), - mScrollable(mHorizontalLayout ? FALSE : p.scrollable), // Scrolling is supported only for vertical layout + mScrollable(mHorizontalLayout ? false : p.scrollable), // Scrolling is supported only for vertical layout mMaxScrollableItems(p.max_scrollable_items), mPreferredWidth(p.preferred_width), mKeepFixedSize( p.keep_fixed_size ), @@ -1771,7 +1771,7 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) mLastMouseY(0), mMouseVelX(0), mMouseVelY(0), - mTornOff(FALSE), + mTornOff(false), mTearOffItem(NULL), mSpilloverBranch(NULL), mFirstVisibleItem(NULL), @@ -1780,8 +1780,8 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) mSpilloverMenu(NULL), mJumpKey(p.jump_key), mCreateJumpKeys(p.create_jump_keys), - mNeedsArrange(FALSE), - mAlwaysShowMenu(FALSE), + mNeedsArrange(false), + mAlwaysShowMenu(false), mResetScrollPositionOnShow(true), mShortcutPad(p.shortcut_pad) { @@ -1821,7 +1821,7 @@ LLMenuGL::~LLMenuGL( void ) mJumpKeys.clear(); } -void LLMenuGL::setCanTearOff(BOOL tear_off) +void LLMenuGL::setCanTearOff(bool tear_off) { if (tear_off && mTearOffItem == NULL) { @@ -1911,7 +1911,7 @@ void LLMenuGL::removeChild( LLView* ctrl) return LLUICtrl::removeChild(ctrl); } -BOOL LLMenuGL::postBuild() +bool LLMenuGL::postBuild() { createJumpKeys(); return LLUICtrl::postBuild(); @@ -1919,10 +1919,10 @@ BOOL LLMenuGL::postBuild() // 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() +bool LLMenuGL::jumpKeysActive() { LLMenuItemGL* highlighted_item = getHighlightedItem(); - BOOL active = getVisible() && getEnabled(); + bool active = getVisible() && getEnabled(); if (active) { @@ -1948,7 +1948,7 @@ BOOL LLMenuGL::jumpKeysActive() return active; } -BOOL LLMenuGL::isOpen() +bool LLMenuGL::isOpen() { if (getTornOff()) { @@ -1957,7 +1957,7 @@ BOOL LLMenuGL::isOpen() // the open menu chain even if we don't have focus if (itemp && itemp->isOpen()) { - return TRUE; + return true; } // otherwise we are only active if we have keyboard focus LLFloater * parent = dynamic_cast(getParent()); @@ -1965,7 +1965,7 @@ BOOL LLMenuGL::isOpen() { return parent->hasFocus(); } - return FALSE; + return false; } else { @@ -2075,7 +2075,7 @@ bool LLMenuGL::scrollItems(EScrollingDirection direction) LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL; } - mNeedsArrange = TRUE; + mNeedsArrange = true; arrangeAndClear(); return true; @@ -2332,11 +2332,11 @@ void LLMenuGL::arrange( void ) LLRect rect; mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight())); - mArrowUpItem->setVisible(TRUE); + mArrowUpItem->setVisible(true); mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING); mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight()); mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight())); - mArrowDownItem->setVisible(TRUE); + mArrowDownItem->setVisible(true); mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height); mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight()); @@ -2348,11 +2348,11 @@ void LLMenuGL::arrange( void ) { if (NULL != mArrowUpItem) { - mArrowUpItem->setVisible(FALSE); + mArrowUpItem->setVisible(false); } if (NULL != mArrowDownItem) { - mArrowDownItem->setVisible(FALSE); + mArrowDownItem->setVisible(false); } } @@ -2415,7 +2415,7 @@ void LLMenuGL::arrangeAndClear( void ) if (mNeedsArrange) { arrange(); - mNeedsArrange = FALSE; + mNeedsArrange = false; } } @@ -2477,7 +2477,7 @@ void LLMenuGL::cleanupSpilloverBranch() void LLMenuGL::createJumpKeys() { if (!mCreateJumpKeys) return; - mCreateJumpKeys = FALSE; + mCreateJumpKeys = false; mJumpKeys.clear(); @@ -2542,7 +2542,7 @@ void LLMenuGL::createJumpKeys() tokenizer tokens(uppercase_label, sep); tokenizer::iterator token_iter; - BOOL found_key = FALSE; + bool found_key = false; for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) { std::string uppercase_word = *token_iter; @@ -2560,7 +2560,7 @@ void LLMenuGL::createJumpKeys() { mJumpKeys.insert(std::pair(jump_key, (*item_it))); (*item_it)->setJumpKey(jump_key); - found_key = TRUE; + found_key = true; break; } } @@ -2643,7 +2643,7 @@ void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom) needsArrange(); } -BOOL LLMenuGL::handleJumpKey(KEY key) +bool LLMenuGL::handleJumpKey(KEY key) { // must perform case-insensitive comparison, so just switch to uppercase input key key = toupper(key); @@ -2651,31 +2651,31 @@ BOOL LLMenuGL::handleJumpKey(KEY key) if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); // force highlight to close old menus and open and sub-menus - found_it->second->setHighlight(TRUE); + found_it->second->setHighlight(true); found_it->second->onCommit(); } // if we are navigating the menus, we need to eat the keystroke // so rest of UI doesn't handle it - return TRUE; + return true; } // Add the menu item to this menu. -BOOL LLMenuGL::append( LLMenuItemGL* item ) +bool LLMenuGL::append( LLMenuItemGL* item ) { - if (!item) return FALSE; + if (!item) return false; mItems.push_back( item ); LLUICtrl::addChild(item); needsArrange(); - return TRUE; + return true; } // add a separator to this menu -BOOL LLMenuGL::addSeparator() +bool LLMenuGL::addSeparator() { LLMenuItemSeparatorGL::Params p; LLMenuItemGL* separator = LLUICtrlFactory::create(p); @@ -2683,14 +2683,14 @@ BOOL LLMenuGL::addSeparator() } // add a menu - this will create a cascading menu -BOOL LLMenuGL::appendMenu( LLMenuGL* menu ) +bool LLMenuGL::appendMenu( LLMenuGL* menu ) { if( menu == this ) { LL_ERRS() << "** Attempt to attach menu to itself. This is certainly " << "a logic error." << LL_ENDL; } - BOOL success = TRUE; + bool success = true; LLMenuItemBranchGL::Params p; p.name = menu->getName(); @@ -2712,7 +2712,7 @@ BOOL LLMenuGL::appendMenu( LLMenuGL* menu ) } // add a context menu branch -BOOL LLMenuGL::appendContextSubMenu(LLMenuGL *menu) +bool LLMenuGL::appendContextSubMenu(LLMenuGL *menu) { if (menu == this) { @@ -2735,7 +2735,7 @@ BOOL LLMenuGL::appendContextSubMenu(LLMenuGL *menu) return append( item ); } -void LLMenuGL::setEnabledSubMenus(BOOL enable) +void LLMenuGL::setEnabledSubMenus(bool enable) { setEnabled(enable); item_list_t::iterator item_iter; @@ -2746,8 +2746,8 @@ void LLMenuGL::setEnabledSubMenus(BOOL enable) } // setItemEnabled() - pass the label and the enable flag for a menu -// item. TRUE will make sure it's enabled, FALSE will disable it. -void LLMenuGL::setItemEnabled( const std::string& name, BOOL enable ) +// item. true will make sure it's enabled, false will disable it. +void LLMenuGL::setItemEnabled( const std::string& name, bool enable ) { item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) @@ -2761,7 +2761,7 @@ void LLMenuGL::setItemEnabled( const std::string& name, BOOL enable ) } } -void LLMenuGL::setItemVisible( const std::string& name, BOOL visible ) +void LLMenuGL::setItemVisible( const std::string& name, bool visible ) { item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) @@ -2796,12 +2796,12 @@ void LLMenuGL::setItemLastSelected(LLMenuItemGL* item) } // Set whether drop shadowed -void LLMenuGL::setDropShadowed( const BOOL shadowed ) +void LLMenuGL::setDropShadowed( const bool shadowed ) { mDropShadowed = shadowed; } -void LLMenuGL::setTornOff(BOOL torn_off) +void LLMenuGL::setTornOff(bool torn_off) { mTornOff = torn_off; } @@ -2854,7 +2854,7 @@ LLMenuItemGL* LLMenuGL::getHighlightedItem() return NULL; } -LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled) +LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled) { if (mItems.empty()) return NULL; // highlighting first item on a torn off menu is the @@ -2864,7 +2864,7 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa LLFloater * parent = dynamic_cast(getParent()); if (parent) { - parent->setFocus(TRUE); + parent->setFocus(true); } } @@ -2933,9 +2933,9 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa { if (cur_item) { - cur_item->setHighlight(FALSE); + cur_item->setHighlight(false); } - (*next_item_iter)->setHighlight(TRUE); + (*next_item_iter)->setHighlight(true); return (*next_item_iter); } @@ -2959,7 +2959,7 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa return NULL; } -LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled) +LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled) { if (mItems.empty()) return NULL; @@ -2970,7 +2970,7 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa LLFloater * parent = dynamic_cast(getParent()); if (parent) { - parent->setFocus(TRUE); + parent->setFocus(true); } } @@ -3025,7 +3025,7 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa // skip separators and disabled/invisible items if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME) { - (*prev_item_iter)->setHighlight(TRUE); + (*prev_item_iter)->setHighlight(true); return (*prev_item_iter); } @@ -3096,12 +3096,12 @@ bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const return false; } -BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) +bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) { // don't handle if not enabled if(!getEnabled()) { - return FALSE; + return false; } // Pass down even if not visible @@ -3111,11 +3111,11 @@ BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) LLMenuItemGL* itemp = *item_iter; if (itemp->handleAcceleratorKey(key, mask)) { - return TRUE; + return true; } } - return FALSE; + return false; } bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char ) @@ -3225,7 +3225,7 @@ void LLMenuGL::draw( void ) if (mNeedsArrange) { arrange(); - mNeedsArrange = FALSE; + mNeedsArrange = false; } if (mDropShadowed && !mTornOff) { @@ -3250,7 +3250,7 @@ void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha) gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0); } -void LLMenuGL::setVisible(BOOL visible) +void LLMenuGL::setVisible(bool visible) { if (visible != getVisible()) { @@ -3273,7 +3273,7 @@ void LLMenuGL::setVisible(BOOL visible) } } -LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) const +LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, bool recurse) const { LLView* view = findChildView(name, recurse); if (view) @@ -3294,23 +3294,23 @@ LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) c return NULL; } -BOOL LLMenuGL::clearHoverItem() +bool LLMenuGL::clearHoverItem() { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it; if (itemp->getHighlight()) { - itemp->setHighlight(FALSE); - return TRUE; + itemp->setHighlight(false); + return true; } } - return FALSE; + return false; } void hide_top_view( LLView* view ) { - if( view ) view->setVisible( FALSE ); + if( view ) view->setVisible( false ); } @@ -3327,12 +3327,12 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S3 return; } - menu->setVisible( TRUE ); + menu->setVisible( true ); if(!menu->getAlwaysShowMenu()) { //Do not show menu if all menu items are disabled - BOOL item_enabled = false; + bool item_enabled = false; for (LLView::child_list_t::const_iterator itor = menu->getChildList()->begin(); itor != menu->getChildList()->end(); ++itor) @@ -3343,7 +3343,7 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S3 if(!item_enabled) { - menu->setVisible( FALSE ); + menu->setVisible( false ); return; } } @@ -3408,7 +3408,7 @@ static LLDefaultChildRegistry::Register r2("menu_bar"); LLMenuBarGL::LLMenuBarGL( const Params& p ) : LLMenuGL(p), - mAltKeyTrigger(FALSE) + mAltKeyTrigger(false) {} // Default destructor @@ -3418,19 +3418,19 @@ LLMenuBarGL::~LLMenuBarGL() mAccelerators.clear(); } -BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) +bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) { if (getHighlightedItem() && mask == MASK_NONE) { // unmodified key accelerators are ignored when navigating menu // (but are used as jump keys so will still work when appropriate menu is up) - return FALSE; + return false; } - BOOL result = LLMenuGL::handleAcceleratorKey(key, mask); + bool result = LLMenuGL::handleAcceleratorKey(key, mask); if (result && mask & MASK_ALT) { // ALT key used to trigger hotkey, don't use as shortcut to open menu - mAltKeyTrigger = FALSE; + mAltKeyTrigger = false; } if(!result @@ -3441,16 +3441,16 @@ BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) if (getHighlightedItem()) { clearHoverItem(); - LLMenuGL::setKeyboardMode(FALSE); + LLMenuGL::setKeyboardMode(false); } else { // close menus originating from other menu bars when first opening menu via keyboard LLMenuGL::sMenuContainer->hideMenus(); highlightNextItem(NULL); - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); } - return TRUE; + return true; } if (result && !getHighlightedItem() && LLMenuGL::sMenuContainer->hasVisibleMenu()) @@ -3462,22 +3462,22 @@ BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) return result; } -BOOL LLMenuBarGL::handleKeyHere(KEY key, MASK mask) +bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask) { static LLUICachedControl use_altkey_for_menus ("UseAltKeyForMenus", 0); if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus) { - mAltKeyTrigger = TRUE; + mAltKeyTrigger = true; } else // if any key other than ALT hit, clear out waiting for Alt key mode { - mAltKeyTrigger = FALSE; + mAltKeyTrigger = false; } if (key == KEY_ESCAPE && mask == MASK_NONE) { - LLMenuGL::setKeyboardMode(FALSE); - // if any menus are visible, this will return TRUE, stopping further processing of ESCAPE key + LLMenuGL::setKeyboardMode(false); + // if any menus are visible, this will return true, stopping further processing of ESCAPE key return LLMenuGL::sMenuContainer->hideMenus(); } @@ -3487,7 +3487,7 @@ BOOL LLMenuBarGL::handleKeyHere(KEY key, MASK mask) return LLMenuGL::handleKeyHere(key, mask); } -BOOL LLMenuBarGL::handleJumpKey(KEY key) +bool LLMenuBarGL::handleJumpKey(KEY key) { // perform case-insensitive comparison key = toupper(key); @@ -3495,12 +3495,12 @@ BOOL LLMenuBarGL::handleJumpKey(KEY key) if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) { // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); - found_it->second->setHighlight(TRUE); + found_it->second->setHighlight(true); found_it->second->onCommit(); } - return TRUE; + return true; } bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) @@ -3559,14 +3559,14 @@ void LLMenuBarGL::checkMenuTrigger() LLMenuGL::sMenuContainer->hideMenus(); highlightNextItem(NULL); - LLMenuGL::setKeyboardMode(TRUE); + LLMenuGL::setKeyboardMode(true); } } - mAltKeyTrigger = FALSE; + mAltKeyTrigger = false; } } -BOOL LLMenuBarGL::jumpKeysActive() +bool LLMenuBarGL::jumpKeysActive() { // require user to be in keyboard navigation mode to activate key triggers // as menu bars are always visible and it is easy to leave the mouse cursor over them @@ -3615,14 +3615,14 @@ S32 LLMenuBarGL::getRightmostMenuEdge() } // add a vertical separator to this menu -BOOL LLMenuBarGL::addSeparator() +bool LLMenuBarGL::addSeparator() { LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL(); return append( separator ); } // add a menu - this will create a drop down menu. -BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu ) +bool LLMenuBarGL::appendMenu( LLMenuGL* menu ) { if( menu == this ) { @@ -3630,7 +3630,7 @@ BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu ) << "a logic error." << LL_ENDL; } - BOOL success = TRUE; + bool success = true; // *TODO: Hack! Fix this LLMenuItemBranchDownGL::Params p; @@ -3657,7 +3657,7 @@ bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) bool handled = false; LLView* active_menu = NULL; - BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; + bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2); @@ -3691,7 +3691,7 @@ bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) viewp->handleHover(local_x, local_y, mask)) { ((LLMenuItemGL*)viewp)->setHighlight(true); - handled = TRUE; + handled = true; if (active_menu && active_menu != viewp) { ((LLMenuItemGL*)viewp)->onCommit(); @@ -3731,7 +3731,7 @@ LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p) : LLPanel(p) { sItemActivationTimer.stop(); - mCanHide = TRUE; + mCanHide = true; } void LLMenuHolderGL::draw() @@ -3823,9 +3823,9 @@ bool LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask ) return handled; } -BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) +bool LLMenuHolderGL::handleKey(KEY key, MASK mask, bool called_from_parent) { - BOOL handled = false; + bool handled = false; LLMenuGL* const pMenu = dynamic_cast(getVisibleMenu()); if (pMenu) @@ -3833,7 +3833,7 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) //eat TAB key - EXT-7000 if (key == KEY_TAB && mask == MASK_NONE) { - return TRUE; + return true; } //handle ESCAPE and RETURN key @@ -3842,7 +3842,7 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) { if (pMenu->getHighlightedItem()) { - handled = pMenu->handleKey(key, mask, TRUE); + handled = pMenu->handleKey(key, mask, true); } else if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN)) { @@ -3859,7 +3859,7 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) } -void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLMenuHolderGL::reshape(S32 width, S32 height, bool called_from_parent) { if (width != getRect().getWidth() || height != getRect().getHeight()) { @@ -3882,14 +3882,14 @@ LLView* const LLMenuHolderGL::getVisibleMenu() const } -BOOL LLMenuHolderGL::hideMenus() +bool LLMenuHolderGL::hideMenus() { if (!mCanHide) { - return FALSE; + return false; } - LLMenuGL::setKeyboardMode(FALSE); - BOOL menu_visible = hasVisibleMenu(); + LLMenuGL::setKeyboardMode(false); + bool menu_visible = hasVisibleMenu(); if (menu_visible) { // clicked off of menu, hide them all @@ -3898,7 +3898,7 @@ BOOL LLMenuHolderGL::hideMenus() LLView* viewp = *child_it; if (dynamic_cast(viewp) != NULL && viewp->getVisible()) { - viewp->setVisible(FALSE); + viewp->setVisible(false); } } } @@ -3927,9 +3927,9 @@ LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : setName(menup->getName()); setTitle(menup->getLabel()); - setCanMinimize(FALSE); + setCanMinimize(false); // flag menu as being torn off - menup->setTornOff(TRUE); + menup->setTornOff(true); // update menu layout as torn off menu (no spillover menus) menup->needsArrange(); @@ -3944,12 +3944,12 @@ LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); mOldParent = menup->getParent(); addChild(menup); - menup->setVisible(TRUE); + menup->setVisible(true); LLRect menu_rect = menup->getRect(); menu_rect.setOriginAndSize( 1, 1, menu_rect.getWidth(), menu_rect.getHeight()); menup->setRect(menu_rect); - menup->setDropShadowed(FALSE); + menup->setDropShadowed(false); mMenu = menup; @@ -3997,7 +3997,7 @@ void LLTearOffMenu::onFocusReceived() { if (parent_menu_item->getMenu()->getVisible()) { - parent_menu_item->setHighlight(TRUE); + parent_menu_item->setHighlight(true); parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem(); } else @@ -4015,29 +4015,29 @@ void LLTearOffMenu::onFocusLost() LLFloater::onFocusLost(); } -BOOL LLTearOffMenu::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent) { // pass keystrokes down to menu - return mMenu->handleUnicodeChar(uni_char, TRUE); + return mMenu->handleUnicodeChar(uni_char, true); } -BOOL LLTearOffMenu::handleKeyHere(KEY key, MASK mask) +bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask) { if (!mMenu->getHighlightedItem()) { if (key == KEY_UP) { mMenu->highlightPrevItem(NULL); - return TRUE; + return true; } else if (key == KEY_DOWN) { mMenu->highlightNextItem(NULL); - return TRUE; + return true; } } // pass keystrokes down to menu - return mMenu->handleKey(key, mask, TRUE); + return mMenu->handleKey(key, mask, true); } void LLTearOffMenu::translate(S32 x, S32 y) @@ -4055,7 +4055,7 @@ LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup) { LLTearOffMenu* tearoffp = new LLTearOffMenu(menup); // keep onscreen - gFloaterView->adjustToFitScreen(tearoffp, FALSE); + gFloaterView->adjustToFitScreen(tearoffp, false); tearoffp->openFloater(LLSD()); return tearoffp; @@ -4092,10 +4092,10 @@ void LLTearOffMenu::closeTearOff() mOldParent->addChild(mMenu); mMenu->clearHoverItem(); mMenu->setFollowsNone(); - mMenu->setBackgroundVisible(TRUE); - mMenu->setVisible(FALSE); - mMenu->setTornOff(FALSE); - mMenu->setDropShadowed(TRUE); + mMenu->setBackgroundVisible(true); + mMenu->setVisible(false); + mMenu->setTornOff(false); + mMenu->setDropShadowed(true); mQuitRequested = true; } @@ -4129,19 +4129,19 @@ void LLContextMenuBranch::buildDrawLabel( void ) // enabled, this item is enabled. JC U32 sub_count = menu->getItemCount(); U32 i; - BOOL any_enabled = FALSE; + bool any_enabled = false; for (i = 0; i < sub_count; i++) { LLMenuItemGL* item = menu->getItem(i); item->buildDrawLabel(); if (item->getEnabled() && !item->getDrawTextDisabled() ) { - any_enabled = TRUE; + any_enabled = true; break; } } setDrawTextDisabled(!any_enabled); - setEnabled(TRUE); + setEnabled(true); } mDrawAccelLabel.clear(); @@ -4174,7 +4174,7 @@ void LLContextMenuBranch::onCommit( void ) showSubMenu(); } -void LLContextMenuBranch::setHighlight( BOOL highlight ) +void LLContextMenuBranch::setHighlight( bool highlight ) { if (highlight == getHighlight()) return; LLMenuItemGL::setHighlight(highlight); @@ -4204,13 +4204,13 @@ static MenuRegistry::Register context_menu_register2("context_men LLContextMenu::LLContextMenu(const Params& p) : LLMenuGL(p), - mHoveredAnyItem(FALSE), + mHoveredAnyItem(false), mHoverItem(NULL) { - //setBackgroundVisible(TRUE); + //setBackgroundVisible(true); } -void LLContextMenu::setVisible(BOOL visible) +void LLContextMenu::setVisible(bool visible) { if (!visible) hide(); @@ -4279,18 +4279,18 @@ void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view) { mSpawningViewHandle.markDead(); } - LLView::setVisible(TRUE); + LLView::setVisible(true); } void LLContextMenu::hide() { if (!getVisible()) return; - LLView::setVisible(FALSE); + LLView::setVisible(false); if (mHoverItem) { - mHoverItem->setHighlight( FALSE ); + mHoverItem->setHighlight( false ); } mHoverItem = NULL; } @@ -4386,7 +4386,7 @@ bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) } - BOOL result = handleMouseUp( x, y, mask ); + bool result = handleMouseUp( x, y, mask ); mHoveredAnyItem = false; return result; diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 88fb30fbb2..a52941ab73 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -89,7 +89,7 @@ protected: friend class LLUICtrlFactory; public: // LLView overrides - /*virtual*/ void onVisibilityChange(BOOL new_visibility); + /*virtual*/ void onVisibilityChange(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); @@ -99,7 +99,7 @@ public: /*virtual*/ LLSD getValue() const; virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; - virtual BOOL handleAcceleratorKey(KEY key, MASK mask); + virtual bool handleAcceleratorKey(KEY key, MASK mask); LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); } @@ -114,17 +114,17 @@ public: virtual U32 getNominalHeight( void ) const; // Marks item as not needing space for check marks or accelerator keys - virtual void setBriefItem(BOOL brief); - virtual BOOL isBriefItem() const; + virtual void setBriefItem(bool brief); + virtual bool isBriefItem() const; - virtual BOOL addToAcceleratorList(std::list *listp); - void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; } - BOOL getAllowKeyRepeat() const { return mAllowKeyRepeat; } + virtual bool addToAcceleratorList(std::list *listp); + void setAllowKeyRepeat(bool allow) { mAllowKeyRepeat = allow; } + bool getAllowKeyRepeat() const { return mAllowKeyRepeat; } // change the label void setLabel( const LLStringExplicit& label ) { mLabel = label; } std::string getLabel( void ) const { return mLabel.getString(); } - virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); // Get the parent menu for this item virtual class LLMenuGL* getMenu() const; @@ -150,19 +150,19 @@ public: virtual void onCommit( void ); - virtual void setHighlight( BOOL highlight ); - virtual BOOL getHighlight() const { return mHighlight; } + virtual void setHighlight( bool highlight ); + virtual bool getHighlight() const { return mHighlight; } // determine if this represents an active sub-menu - virtual BOOL isActive( void ) const { return FALSE; } + virtual bool isActive( void ) const { return false; } // determine if this represents an open sub-menu - virtual BOOL isOpen( void ) const { return FALSE; } + virtual bool isOpen( void ) const { return false; } - virtual void setEnabledSubMenus(BOOL enable){}; + virtual void setEnabledSubMenus(bool enable){}; // LLView Functionality - virtual BOOL handleKeyHere( KEY key, MASK mask ); + 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 ); @@ -172,13 +172,13 @@ public: virtual void draw( void ); - BOOL getHover() const { return mGotHover; } + bool getHover() const { return mGotHover; } - void setDrawTextDisabled(BOOL disabled) { mDrawTextDisabled = disabled; } - BOOL getDrawTextDisabled() const { return mDrawTextDisabled; } + void setDrawTextDisabled(bool disabled) { mDrawTextDisabled = disabled; } + bool getDrawTextDisabled() const { return mDrawTextDisabled; } protected: - void setHover(BOOL hover) { mGotHover = hover; } + void setHover(bool hover) { mGotHover = hover; } // This function appends the character string representation of // the current accelerator key and mask to the provided string. @@ -207,19 +207,19 @@ protected: LLUIColor mHighlightBackground; LLUIColor mHighlightForeground; - BOOL mHighlight; + bool mHighlight; private: // Keyboard and mouse variables - BOOL mAllowKeyRepeat; - BOOL mGotHover; + bool mAllowKeyRepeat; + bool mGotHover; // If true, suppress normal space for check marks on the left and accelerator // keys on the right. - BOOL mBriefItem; + bool mBriefItem; // Font for this item const LLFontGL* mFont; - BOOL mDrawTextDisabled; + bool mDrawTextDisabled; KEY mJumpKey; }; @@ -288,8 +288,8 @@ public: virtual void onCommit( void ); - virtual BOOL handleAcceleratorKey(KEY key, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleAcceleratorKey(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); //virtual void draw(); @@ -447,18 +447,18 @@ public: /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks ); /*virtual*/ void draw( void ); /*virtual*/ void drawBackground(LLMenuItemGL* itemp, F32 alpha); - /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setVisible(bool visible); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); /*virtual*/ void deleteAllChildren(); /*virtual*/ void removeChild( LLView* ctrl); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; - virtual BOOL handleAcceleratorKey(KEY key, MASK mask); + virtual bool handleAcceleratorKey(KEY key, MASK mask); - LLMenuGL* findChildMenuByName(const std::string& name, BOOL recurse) const; + LLMenuGL* findChildMenuByName(const std::string& name, bool recurse) const; - BOOL clearHoverItem(); + bool clearHoverItem(); // return the name label const std::string& getLabel( void ) const { return mLabel.getString(); } @@ -467,37 +467,37 @@ public: // background colors void setBackgroundColor( const LLUIColor& color ) { mBackgroundColor = color; } const LLUIColor& getBackgroundColor() const { return mBackgroundColor; } - void setBackgroundVisible( BOOL b ) { mBgVisible = b; } - void setCanTearOff(BOOL tear_off); + void setBackgroundVisible( bool b ) { mBgVisible = b; } + void setCanTearOff(bool tear_off); // add a separator to this menu - virtual BOOL addSeparator(); + virtual bool addSeparator(); // for branching menu items, bring sub menus up to root level of menu hierarchy virtual void updateParent( LLView* parentp ); // setItemEnabled() - pass the name and the enable flag for a - // menu item. TRUE will make sure it's enabled, FALSE will disable + // menu item. true will make sure it's enabled, false will disable // it. - void setItemEnabled( const std::string& name, BOOL enable ); + void setItemEnabled( const std::string& name, bool enable ); // propagate message to submenus - void setEnabledSubMenus(BOOL enable); + void setEnabledSubMenus(bool enable); - void setItemVisible( const std::string& name, BOOL visible); + void setItemVisible( const std::string& name, bool visible); void setItemLabel(const std::string &name, const std::string &label); // sets the left,bottom corner of menu, useful for popups void setLeftAndBottom(S32 left, S32 bottom); - virtual BOOL handleJumpKey(KEY key); + virtual bool handleJumpKey(KEY key); - virtual BOOL jumpKeysActive(); + virtual bool jumpKeysActive(); - virtual BOOL isOpen(); + virtual bool isOpen(); - void needsArrange() { mNeedsArrange = TRUE; } + 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 @@ -520,8 +520,8 @@ public: LLMenuItemGL* getItem(std::string name); LLMenuItemGL* getHighlightedItem(); - LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled = TRUE); - LLMenuItemGL* highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled = TRUE); + LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled = true); + LLMenuItemGL* highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled = true); void buildDrawLabels(); void createJumpKeys(); @@ -530,46 +530,46 @@ public: static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x = 0, S32 mouse_y = 0); // Whether to drop shadow menu bar - void setDropShadowed( const BOOL shadowed ); + void setDropShadowed( const bool shadowed ); 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; } + void setTornOff(bool torn_off); + bool getTornOff() { return mTornOff; } - BOOL getCanTearOff() { return mTearOffItem != NULL; } + bool getCanTearOff() { return mTearOffItem != NULL; } KEY getJumpKey() const { return mJumpKey; } void setJumpKey(KEY key) { mJumpKey = key; } - static void setKeyboardMode(BOOL mode) { sKeyboardMode = mode; } - static BOOL getKeyboardMode() { return sKeyboardMode; } + static void setKeyboardMode(bool mode) { sKeyboardMode = mode; } + static bool getKeyboardMode() { return sKeyboardMode; } S32 getShortcutPad() { return mShortcutPad; } bool scrollItems(EScrollingDirection direction); - BOOL isScrollable() const { return mScrollable; } + bool isScrollable() const { return mScrollable; } static class LLMenuHolderGL* sMenuContainer; void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; } bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; } - void setAlwaysShowMenu(BOOL show) { mAlwaysShowMenu = show; } - BOOL getAlwaysShowMenu() { return mAlwaysShowMenu; } + void setAlwaysShowMenu(bool show) { mAlwaysShowMenu = show; } + bool getAlwaysShowMenu() { return mAlwaysShowMenu; } // add a context menu branch - BOOL appendContextSubMenu(LLMenuGL *menu); + bool appendContextSubMenu(LLMenuGL *menu); protected: void createSpilloverBranch(); void cleanupSpilloverBranch(); // Add the menu item to this menu. - virtual BOOL append( LLMenuItemGL* item ); + virtual bool append( LLMenuItemGL* item ); // add a menu - this will create a cascading menu - virtual BOOL appendMenu( LLMenuGL* menu ); + virtual bool appendMenu( LLMenuGL* menu ); // Used in LLContextMenu and in LLTogleableMenu // to add an item of context menu branch @@ -589,33 +589,33 @@ protected: S32 mMouseVelY; U32 mMaxScrollableItems; U32 mPreferredWidth; - BOOL mHorizontalLayout; - BOOL mScrollable; - BOOL mKeepFixedSize; - BOOL mNeedsArrange; + bool mHorizontalLayout; + bool mScrollable; + bool mKeepFixedSize; + bool mNeedsArrange; private: static LLColor4 sDefaultBackgroundColor; - static BOOL sKeyboardMode; + static bool sKeyboardMode; - BOOL mAlwaysShowMenu; + bool mAlwaysShowMenu; LLUIColor mBackgroundColor; - BOOL mBgVisible; + bool mBgVisible; LLHandle mParentMenuItem; LLUIString mLabel; - BOOL mDropShadowed; // Whether to drop shadow + bool mDropShadowed; // Whether to drop shadow bool mHasSelection; LLFrameTimer mFadeTimer; LLTimer mScrollItemsTimer; - BOOL mTornOff; + bool mTornOff; class LLMenuItemTearOffGL* mTearOffItem; class LLMenuItemBranchGL* mSpilloverBranch; LLMenuGL* mSpilloverMenu; KEY mJumpKey; - BOOL mCreateJumpKeys; + bool mCreateJumpKeys; S32 mShortcutPad; bool mResetScrollPositionOnShow; }; // end class LLMenuGL @@ -646,44 +646,44 @@ public: virtual bool handleMouseUp(S32 x, S32 y, MASK mask); virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; - virtual BOOL handleAcceleratorKey(KEY key, MASK mask); + virtual bool handleAcceleratorKey(KEY key, MASK mask); // check if we've used these accelerators already - virtual BOOL addToAcceleratorList(std::list *listp); + virtual bool addToAcceleratorList(std::list *listp); // called to rebuild the draw label virtual void buildDrawLabel( void ); virtual void onCommit( void ); - virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); - virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); + virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); // set the hover status (called by it's menu) and if the object is // active. This is used for behavior transfer. - virtual void setHighlight( BOOL highlight ); + virtual void setHighlight( bool highlight ); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); - virtual BOOL isActive() const; + virtual bool isActive() const; - virtual BOOL isOpen() const; + virtual bool isOpen() const; LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); } virtual void updateBranchParent( LLView* parentp ); // LLView Functionality - virtual void onVisibilityChange( BOOL curVisibilityIn ); + virtual void onVisibilityChange( bool curVisibilityIn ); virtual void draw(); - virtual void setEnabledSubMenus(BOOL enabled) { if (getBranch()) getBranch()->setEnabledSubMenus(enabled); } + virtual void setEnabledSubMenus(bool enabled) { if (getBranch()) getBranch()->setEnabledSubMenus(enabled); } virtual void openMenu(); - virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const; - virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const; + virtual LLView* getChildView(const std::string& name, bool recurse = true) const; + virtual LLView* findChildView(const std::string& name, bool recurse = true) const; private: LLHandle mBranchHandle; @@ -716,7 +716,7 @@ public: // LLView Functionality // can't set visibility directly, must call show or hide - virtual void setVisible (BOOL visible); + virtual void setVisible (bool visible); virtual void show (S32 x, S32 y, LLView* spawning_view = NULL); virtual void hide (); @@ -733,7 +733,7 @@ public: void setSpawningView(LLHandle spawning_view) { mSpawningViewHandle = spawning_view; } protected: - BOOL mHoveredAnyItem; + bool mHoveredAnyItem; LLMenuItemGL* mHoverItem; LLRootHandle mHandle; LLHandle mSpawningViewHandle; @@ -762,7 +762,7 @@ public: virtual void onCommit( void ); LLContextMenu* getBranch() { return mBranch.get(); } - void setHighlight( BOOL highlight ); + void setHighlight( bool highlight ); protected: void showSubMenu(); @@ -785,17 +785,17 @@ public: LLMenuBarGL( const Params& p ); virtual ~LLMenuBarGL(); - /*virtual*/ BOOL handleAcceleratorKey(KEY key, MASK mask); - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); - /*virtual*/ BOOL handleJumpKey(KEY key); + /*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 handleDoubleClick(S32 x, S32 y, MASK mask); /*virtual*/ void draw(); - /*virtual*/ BOOL jumpKeysActive(); + /*virtual*/ bool jumpKeysActive(); // add a vertical separator to this menu - virtual BOOL addSeparator(); + virtual bool addSeparator(); // LLView Functionality virtual bool handleHover( S32 x, S32 y, MASK mask ); @@ -803,11 +803,11 @@ public: // Returns x position of rightmost child, usually Help menu S32 getRightmostMenuEdge(); - void resetMenuTrigger() { mAltKeyTrigger = FALSE; } + void resetMenuTrigger() { mAltKeyTrigger = false; } private: // add a menu - this will create a drop down menu. - virtual BOOL appendMenu( LLMenuGL* menu ); + virtual bool appendMenu( LLMenuGL* menu ); // rearrange the child rects so they fit the shape of the menu // bar. virtual void arrange( void ); @@ -815,7 +815,7 @@ private: void checkMenuTrigger(); std::list mAccelerators; - BOOL mAltKeyTrigger; + bool mAltKeyTrigger; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -831,9 +831,9 @@ public: LLMenuHolderGL(const Params& p); virtual ~LLMenuHolderGL() {} - virtual BOOL hideMenus(); - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - void setCanHide(BOOL can_hide) { mCanHide = can_hide; } + virtual bool hideMenus(); + void reshape(S32 width, S32 height, bool called_from_parent = true); + void setCanHide(bool can_hide) { mCanHide = can_hide; } // LLView functionality virtual void draw(); @@ -843,10 +843,10 @@ public: // 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 bool handleKey(KEY key, MASK mask, bool called_from_parent); virtual const LLRect getMenuRect() const { return getLocalRect(); } LLView*const getVisibleMenu() const; - virtual BOOL hasVisibleMenu() const {return getVisibleMenu() != NULL;} + virtual bool hasVisibleMenu() const {return getVisibleMenu() != NULL;} static void setActivatedItem(LLMenuItemGL* item); @@ -858,7 +858,7 @@ private: static LLHandle sItemLastSelectedHandle; static LLFrameTimer sItemActivationTimer; - BOOL mCanHide; + bool mCanHide; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -876,8 +876,8 @@ public: virtual void draw(void); virtual void onFocusReceived(); virtual void onFocusLost(); - virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); + virtual bool handleKeyHere(KEY key, MASK mask); virtual void translate(S32 x, S32 y); void updateSize(); diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index d3afbdb8ba..c501d1efc1 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -38,18 +38,18 @@ // static std::list LLModalDialog::sModalStack; -LLModalDialog::LLModalDialog( const LLSD& key, BOOL modal ) +LLModalDialog::LLModalDialog( const LLSD& key, bool modal ) : LLFloater(key), mModal( modal ) { if (modal) { - setCanMinimize(FALSE); - setCanClose(FALSE); + setCanMinimize(false); + setCanClose(false); } - setVisible( FALSE ); - setBackgroundVisible(TRUE); - setBackgroundOpaque(TRUE); + setVisible( false ); + setBackgroundVisible(true); + setBackgroundOpaque(true); centerOnScreen(); // default position mCloseSignal.connect(boost::bind(&LLModalDialog::stopModal, this)); } @@ -70,7 +70,7 @@ LLModalDialog::~LLModalDialog() } // virtual -BOOL LLModalDialog::postBuild() +bool LLModalDialog::postBuild() { return LLFloater::postBuild(); } @@ -85,7 +85,7 @@ void LLModalDialog::openFloater(const LLSD& key) LLFloater::setFloaterHost(thost); } -void LLModalDialog::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLModalDialog::reshape(S32 width, S32 height, bool called_from_parent) { LLFloater::reshape(width, height, called_from_parent); centerOnScreen(); @@ -102,14 +102,14 @@ void LLModalDialog::onOpen(const LLSD& key) LLModalDialog* front = sModalStack.front(); if (front != this) { - front->setVisible(FALSE); + front->setVisible(false); } } // This is a modal dialog. It sucks up all mouse and keyboard operations. gFocusMgr.setMouseCapture( this ); LLUI::getInstance()->addPopup(this); - setFocus(TRUE); + setFocus(true); std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); if (iter != sModalStack.end()) @@ -142,12 +142,12 @@ void LLModalDialog::stopModal() if (!sModalStack.empty()) { LLModalDialog* front = sModalStack.front(); - front->setVisible(TRUE); + front->setVisible(true); } } -void LLModalDialog::setVisible( BOOL visible ) +void LLModalDialog::setVisible( bool visible ) { if (mModal) { @@ -158,7 +158,7 @@ void LLModalDialog::setVisible( BOOL visible ) // The dialog view is a root view LLUI::getInstance()->addPopup(this); - setFocus( TRUE ); + setFocus( true ); } else { @@ -256,27 +256,27 @@ bool LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask) } -BOOL LLModalDialog::handleKeyHere(KEY key, MASK mask ) +bool LLModalDialog::handleKeyHere(KEY key, MASK mask ) { LLFloater::handleKeyHere(key, mask ); if (mModal) { // Suck up all keystokes except CTRL-Q. - BOOL is_quit = ('Q' == key) && (MASK_CONTROL == mask); + bool is_quit = ('Q' == key) && (MASK_CONTROL == mask); return !is_quit; } else { // don't process escape key until message box has been on screen a minimal amount of time // to avoid accidentally destroying the message box when user is hitting escape at the time it appears - BOOL enough_time_elapsed = mVisibleTime.getElapsedTimeF32() > 1.0f; + bool enough_time_elapsed = mVisibleTime.getElapsedTimeF32() > 1.0f; if (enough_time_elapsed && key == KEY_ESCAPE) { closeFloater(); - return TRUE; + return true; } - return FALSE; + return false; } } @@ -312,7 +312,7 @@ void LLModalDialog::onAppFocusLost() gFocusMgr.setMouseCapture( NULL ); } - instance->setFocus(FALSE); + instance->setFocus(false); } } @@ -325,7 +325,7 @@ void LLModalDialog::onAppFocusGained() // This is a modal dialog. It sucks up all mouse and keyboard operations. gFocusMgr.setMouseCapture( instance ); - instance->setFocus(TRUE); + instance->setFocus(true); LLUI::getInstance()->addPopup(instance); instance->centerOnScreen(); diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h index 1c7f86a17e..0fd1f64033 100644 --- a/indra/llui/llmodaldialog.h +++ b/indra/llui/llmodaldialog.h @@ -39,15 +39,15 @@ class LLModalDialog; class LLModalDialog : public LLFloater { public: - LLModalDialog( const LLSD& key, BOOL modal = true ); + LLModalDialog( const LLSD& key, bool modal = true ); virtual ~LLModalDialog(); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); /*virtual*/ void openFloater(const LLSD& key = LLSD()); /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); @@ -55,12 +55,12 @@ public: /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask ); - /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setVisible(bool visible); /*virtual*/ void draw(); - BOOL isModal() const { return mModal; } + bool isModal() const { return mModal; } void stopModal(); static void onAppFocusLost(); @@ -75,7 +75,7 @@ protected: private: LLFrameTimer mVisibleTime; - const BOOL mModal; + const bool mModal; static std::list sModalStack; // Top of stack is currently being displayed }; diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp index d1a597511e..fe0d88e68f 100644 --- a/indra/llui/llmultifloater.cpp +++ b/indra/llui/llmultifloater.cpp @@ -40,7 +40,7 @@ LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params) : LLFloater(key), mTabContainer(NULL), mTabPos(LLTabContainer::TOP), - mAutoResize(TRUE), + mAutoResize(true), mOrigMinWidth(params.min_width), mOrigMinHeight(params.min_height) { @@ -71,7 +71,7 @@ void LLMultiFloater::onClose(bool app_quitting) { if(isMinimized()) { - setMinimized(FALSE); + setMinimized(false); } LLFloater::onClose(app_quitting); } @@ -89,7 +89,7 @@ void LLMultiFloater::draw() } } -BOOL LLMultiFloater::closeAllFloaters() +bool LLMultiFloater::closeAllFloaters() { S32 tabToClose = 0; S32 lastTabCount = mTabContainer->getTabCount(); @@ -110,8 +110,8 @@ BOOL LLMultiFloater::closeAllFloaters() } } if( mTabContainer->getTabCount() != 0 ) - return FALSE; // Couldn't close all the tabs (pending save dialog?) so return FALSE. - return TRUE; //else all tabs were successfully closed... + return false; // Couldn't close all the tabs (pending save dialog?) so return false. + return true; //else all tabs were successfully closed... } void LLMultiFloater::growToFit(S32 content_width, S32 content_height) @@ -139,7 +139,7 @@ void LLMultiFloater::growToFit(S32 content_width, S32 content_height) } /** - void addFloater(LLFloater* floaterp, BOOL select_added_floater) + void addFloater(LLFloater* floaterp, bool select_added_floater) Adds the LLFloater pointed to by floaterp to this. If floaterp is already hosted by this, then it is re-added to get @@ -149,7 +149,7 @@ void LLMultiFloater::growToFit(S32 content_width, S32 content_height) Affects: mTabContainer, floaterp **/ -void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point) +void LLMultiFloater::addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point) { if (!floaterp) { @@ -190,13 +190,13 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, floater_data.mSaveRect = floaterp->mSaveRect; // remove minimize and close buttons - floaterp->setCanMinimize(FALSE); - floaterp->setCanResize(FALSE); - floaterp->setCanDrag(FALSE); - floaterp->mSaveRect = FALSE; + floaterp->setCanMinimize(false); + floaterp->setCanResize(false); + floaterp->setCanDrag(false); + floaterp->mSaveRect = false; floaterp->storeRectControl(); // avoid double rendering of floater background (makes it more opaque) - floaterp->setBackgroundVisible(FALSE); + floaterp->setBackgroundVisible(false); if (mAutoResize) { @@ -226,7 +226,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, floaterp->setHost(this); if (isMinimized()) { - floaterp->setVisible(FALSE); + floaterp->setVisible(false); } // Tabs sometimes overlap resize handle @@ -244,14 +244,14 @@ void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp) /** - BOOL selectFloater(LLFloater* floaterp) + bool selectFloater(LLFloater* floaterp) If the LLFloater pointed to by floaterp is hosted by this, then its tab is selected and returns true. Otherwise returns false. Affects: mTabContainer **/ -BOOL LLMultiFloater::selectFloater(LLFloater* floaterp) +bool LLMultiFloater::selectFloater(LLFloater* floaterp) { return mTabContainer->selectTabPanel(floaterp); } @@ -278,7 +278,7 @@ void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertion if (floaterp != mTabContainer->getCurrentPanel() && !mTabContainer->selectTabPanel(floaterp)) { - addFloater(floaterp, TRUE, insertion_point); + addFloater(floaterp, true, insertion_point); } } @@ -302,8 +302,8 @@ void LLMultiFloater::removeFloater(LLFloater* floaterp) mFloaterDataMap.erase(found_data_it); } mTabContainer->removeTabPanel(floaterp); - floaterp->setBackgroundVisible(TRUE); - floaterp->setCanDrag(TRUE); + floaterp->setBackgroundVisible(true); + floaterp->setCanDrag(true); floaterp->setHost(NULL); floaterp->applyRectControl(); @@ -326,7 +326,7 @@ void LLMultiFloater::tabClose() } } -void LLMultiFloater::setVisible(BOOL visible) +void LLMultiFloater::setVisible(bool visible) { // *FIX: shouldn't have to do this, fix adding to minimized multifloater LLFloater::setVisible(visible); @@ -349,7 +349,7 @@ void LLMultiFloater::setVisible(BOOL visible) } } -BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask) +bool LLMultiFloater::handleKeyHere(KEY key, MASK mask) { if (key == 'W' && mask == MASK_CONTROL) { @@ -363,10 +363,10 @@ BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask) // bring back focus on tab container if there are any tab left if(mTabContainer->getTabCount() > 0) { - mTabContainer->setFocus(TRUE); + mTabContainer->setFocus(true); } } - return TRUE; + return true; } return LLFloater::handleKeyHere(key, mask); @@ -396,7 +396,7 @@ S32 LLMultiFloater::getFloaterCount() } /** - BOOL isFloaterFlashing(LLFloater* floaterp) + bool isFloaterFlashing(LLFloater* floaterp) Returns true if the LLFloater pointed to by floaterp is currently in a flashing state and is hosted by this. @@ -404,24 +404,24 @@ S32 LLMultiFloater::getFloaterCount() Requires: floaterp != NULL **/ -BOOL LLMultiFloater::isFloaterFlashing(LLFloater* floaterp) +bool LLMultiFloater::isFloaterFlashing(LLFloater* floaterp) { if ( floaterp && floaterp->getHost() == this ) return mTabContainer->getTabPanelFlashing(floaterp); - return FALSE; + return false; } /** - BOOL setFloaterFlashing(LLFloater* floaterp, BOOL flashing) + bool setFloaterFlashing(LLFloater* floaterp, bool flashing) Sets the current flashing state of the LLFloater pointed - to by floaterp to be the BOOL flashing if the LLFloater pointed + to by floaterp to be the bool flashing if the LLFloater pointed to by floaterp is hosted by this. Requires: floaterp != NULL **/ -void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, BOOL flashing) +void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, bool flashing) { if ( floaterp && floaterp->getHost() == this ) mTabContainer->setTabPanelFlashing(floaterp, flashing); @@ -436,7 +436,7 @@ void LLMultiFloater::onTabSelected() } } -void LLMultiFloater::setCanResize(BOOL can_resize) +void LLMultiFloater::setCanResize(bool can_resize) { LLFloater::setCanResize(can_resize); if (!mTabContainer) return; @@ -450,7 +450,7 @@ void LLMultiFloater::setCanResize(BOOL can_resize) } } -BOOL LLMultiFloater::postBuild() +bool LLMultiFloater::postBuild() { mCloseSignal.connect(boost::bind(&LLMultiFloater::closeAllFloaters, this)); @@ -459,13 +459,13 @@ BOOL LLMultiFloater::postBuild() if (mTabContainer) { - return TRUE; + return true; } mTabContainer = getChild("Preview Tabs"); setCanResize(mResizable); - return TRUE; + return true; } void LLMultiFloater::updateResizeLimits() @@ -498,7 +498,7 @@ void LLMultiFloater::updateResizeLimits() // make sure this window is visible on screen when it has been modified // (tab added, etc) - gFloaterView->adjustToFitScreen(this, TRUE); + gFloaterView->adjustToFitScreen(this, true); } } diff --git a/indra/llui/llmultifloater.h b/indra/llui/llmultifloater.h index c106a62527..47f7b3e8b9 100644 --- a/indra/llui/llmultifloater.h +++ b/indra/llui/llmultifloater.h @@ -43,16 +43,16 @@ public: void buildTabContainer(); - virtual BOOL postBuild(); + virtual bool postBuild(); /*virtual*/ void onClose(bool app_quitting); virtual void draw(); - virtual void setVisible(BOOL visible); - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); + virtual void setVisible(bool visible); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); - virtual void setCanResize(BOOL can_resize); + virtual void setCanResize(bool can_resize); virtual void growToFit(S32 content_width, S32 content_height); - virtual void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + virtual void addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); virtual void showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); virtual void removeFloater(LLFloater* floaterp); @@ -60,16 +60,16 @@ public: virtual void tabOpen(LLFloater* opened_floater, bool from_click); virtual void tabClose(); - virtual BOOL selectFloater(LLFloater* floaterp); + virtual bool selectFloater(LLFloater* floaterp); virtual void selectNextFloater(); virtual void selectPrevFloater(); virtual LLFloater* getActiveFloater(); - virtual BOOL isFloaterFlashing(LLFloater* floaterp); + virtual bool isFloaterFlashing(LLFloater* floaterp); virtual S32 getFloaterCount(); - virtual void setFloaterFlashing(LLFloater* floaterp, BOOL flashing); - virtual BOOL closeAllFloaters(); //Returns FALSE if the floater could not be closed due to pending confirmation dialogs + virtual void setFloaterFlashing(LLFloater* floaterp, bool flashing); + virtual bool closeAllFloaters(); //Returns false if the floater could not be closed due to pending confirmation dialogs void setTabContainer(LLTabContainer* tab_container) { if (!mTabContainer) mTabContainer = tab_container; } void onTabSelected(); @@ -81,9 +81,9 @@ protected: { S32 mWidth; S32 mHeight; - BOOL mCanMinimize; - BOOL mCanResize; - BOOL mSaveRect; + bool mCanMinimize; + bool mCanResize; + bool mSaveRect; }; LLTabContainer* mTabContainer; @@ -92,7 +92,7 @@ protected: floater_data_map_t mFloaterDataMap; LLTabContainer::TabPosition mTabPos; - BOOL mAutoResize; + bool mAutoResize; S32 mOrigMinWidth, mOrigMinHeight; // logically const but initialized late private: diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index 577e1f0a27..685b1c8b98 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -161,7 +161,7 @@ F32 LLMultiSlider::getNearestIncrement(F32 value) const return mMinValue + value; } -void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from_event) +void LLMultiSlider::setSliderValue(const std::string& name, F32 value, bool from_event) { // exit if not there if(!mValue.has(name)) { @@ -263,7 +263,7 @@ void LLMultiSlider::setValue(const LLSD& value) mCurSlider = mIt->first; for(; mIt != value.endMap(); mIt++) { - setSliderValue(mIt->first, (F32)mIt->second.asReal(), TRUE); + setSliderValue(mIt->first, (F32)mIt->second.asReal(), true); } } } @@ -378,7 +378,7 @@ const std::string& LLMultiSlider::addSlider(F32 val) mCurSlider = newName.str(); // move the slider - setSliderValue(mCurSlider, initVal, TRUE); + setSliderValue(mCurSlider, initVal, true); return mCurSlider; } @@ -411,7 +411,7 @@ bool LLMultiSlider::addSlider(F32 val, const std::string& name) mCurSlider = name; // move the slider - setSliderValue(mCurSlider, initVal, TRUE); + setSliderValue(mCurSlider, initVal, true); return true; } @@ -614,25 +614,25 @@ bool LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask) return true; } -BOOL LLMultiSlider::handleKeyHere(KEY key, MASK mask) +bool LLMultiSlider::handleKeyHere(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; switch(key) { case KEY_UP: case KEY_DOWN: // eat up and down keys to be consistent - handled = TRUE; + handled = true; break; case KEY_LEFT: setCurSliderValue(getCurSliderValue() - getIncrement()); onCommit(); - handled = TRUE; + handled = true; break; case KEY_RIGHT: setCurSliderValue(getCurSliderValue() + getIncrement()); onCommit(); - handled = TRUE; + handled = true; break; default: break; @@ -700,7 +700,7 @@ void LLMultiSlider::draw() mIt->second.mTop + extra_triangle_height, mIt->second.mLeft + mIt->second.getWidth() / 2, mIt->second.mBottom - extra_triangle_height, - mTriangleColor.get() % opacity, TRUE); + mTriangleColor.get() % opacity, true); } } else if (!mRoundedSquareImgp && !mThumbImagep) @@ -725,23 +725,23 @@ void LLMultiSlider::draw() } // the draw command - gl_rect_2d(mIt->second, curThumbColor, TRUE); + gl_rect_2d(mIt->second, curThumbColor, true); } // now draw the current and hover sliders if(curSldrIt != mThumbRects.end()) { - gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), TRUE); + gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), true); } // and draw the drag start if (gFocusMgr.getMouseCapture() == this) { - gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, FALSE); + gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, false); } else if (hoverSldrIt != mThumbRects.end()) { - gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), TRUE); + gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), true); } } else diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h index b6eacd33f5..7195c5d5a3 100644 --- a/indra/llui/llmultislider.h +++ b/indra/llui/llmultislider.h @@ -81,7 +81,7 @@ public: // Multi-slider rounds values to nearest increments (bias towards rounding down) F32 getNearestIncrement(F32 value) const; - void setSliderValue(const std::string& name, F32 value, BOOL from_event = FALSE); + void setSliderValue(const std::string& name, F32 value, bool from_event = false); F32 getSliderValue(const std::string& name) const; F32 getSliderValueFromPos(S32 xpos, S32 ypos) const; LLRect getSliderThumbRect(const std::string& name) const; @@ -94,7 +94,7 @@ public: F32 getCurSliderValue() const { return getSliderValue(mCurSlider); } void setCurSlider(const std::string& name); void resetCurSlider(); - void setCurSliderValue(F32 val, BOOL from_event = false) { setSliderValue(mCurSlider, val, from_event); } + void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mCurSlider, val, from_event); } /*virtual*/ void setValue(const LLSD& value) override; /*virtual*/ LLSD getValue() const override { return mValue; } @@ -113,7 +113,7 @@ public: /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask) override; + /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override; /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; /*virtual*/ void draw() override; @@ -130,11 +130,11 @@ protected: static S32 mNameCounter; S32 mMaxNumSliders; - BOOL mAllowOverlap; - BOOL mLoopOverlap; + bool mAllowOverlap; + bool mLoopOverlap; F32 mOverlapThreshold; - BOOL mDrawTrack; - BOOL mUseTriangle; /// hacked in toggle to use a triangle + bool mDrawTrack; + bool mUseTriangle; /// hacked in toggle to use a triangle S32 mMouseOffset; LLRect mDragStartThumbRect; diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index b3df7c154b..7f5b0ccac3 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -144,7 +144,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p) mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) ); // don't do this, as selecting the entire text is single clicking in some cases // and double clicking in others - //mEditor->setSelectAllonFocusReceived(TRUE); + //mEditor->setSelectAllonFocusReceived(true); addChild(mEditor); } else @@ -219,7 +219,7 @@ void LLMultiSliderCtrl::setValue(const LLSD& value) updateText(); } -void LLMultiSliderCtrl::setSliderValue(const std::string& name, F32 v, BOOL from_event) +void LLMultiSliderCtrl::setSliderValue(const std::string& name, F32 v, bool from_event) { mMultiSlider->setSliderValue(name, v, from_event ); mCurValue = mMultiSlider->getCurSliderValue(); @@ -237,9 +237,9 @@ void LLMultiSliderCtrl::resetCurSlider() mMultiSlider->resetCurSlider(); } -BOOL LLMultiSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLMultiSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) { - BOOL res = FALSE; + bool res = false; if (mLabelBox) { res = mLabelBox->setTextArg(key, text); @@ -329,7 +329,7 @@ void LLMultiSliderCtrl::clear() } -BOOL LLMultiSliderCtrl::isMouseHeldDown() +bool LLMultiSliderCtrl::isMouseHeldDown() { return gFocusMgr.getMouseCapture() == mMultiSlider; } @@ -368,7 +368,7 @@ void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata) if (!self) // cast failed - wrong type! :O return; - BOOL success = FALSE; + bool success = false; F32 val = self->mCurValue; F32 saved_val = self->mCurValue; @@ -382,7 +382,7 @@ void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata) self->setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it. if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) ) { - success = TRUE; + success = true; } } } @@ -409,14 +409,14 @@ void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata) if (!self) return; - BOOL success = FALSE; + bool success = false; F32 saved_val = self->mCurValue; F32 new_val = self->mMultiSlider->getCurSliderValue(); self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it. if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) ) { - success = TRUE; + success = true; } if( success ) @@ -434,7 +434,7 @@ void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata) self->updateText(); } -void LLMultiSliderCtrl::setEnabled(BOOL b) +void LLMultiSliderCtrl::setEnabled(bool b) { LLF32UICtrl::setEnabled( b ); @@ -457,7 +457,7 @@ void LLMultiSliderCtrl::setEnabled(BOOL b) } -void LLMultiSliderCtrl::setTentative(BOOL b) +void LLMultiSliderCtrl::setTentative(bool b) { if( mEditor ) { @@ -469,11 +469,11 @@ void LLMultiSliderCtrl::setTentative(BOOL b) void LLMultiSliderCtrl::onCommit() { - setTentative(FALSE); + setTentative(false); if( mEditor ) { - mEditor->setTentative(FALSE); + mEditor->setTentative(false); } setControlValue(getValueF32()); diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h index adb28676ec..b58540666b 100644 --- a/indra/llui/llmultisliderctrl.h +++ b/indra/llui/llmultisliderctrl.h @@ -83,24 +83,24 @@ public: virtual ~LLMultiSliderCtrl(); F32 getSliderValue(const std::string& name) const { return mMultiSlider->getSliderValue(name); } - void setSliderValue(const std::string& name, F32 v, BOOL from_event = FALSE); + void setSliderValue(const std::string& name, F32 v, bool from_event = false); virtual void setValue(const LLSD& value ); virtual LLSD getValue() const { return mMultiSlider->getValue(); } - virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); const std::string& getCurSlider() const { return mMultiSlider->getCurSlider(); } F32 getCurSliderValue() const { return mCurValue; } void setCurSlider(const std::string& name); void resetCurSlider(); - void setCurSliderValue(F32 val, BOOL from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); } + void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); } virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } - BOOL isMouseHeldDown(); + bool isMouseHeldDown(); - virtual void setEnabled( BOOL b ); + virtual void setEnabled( bool b ); virtual void clear(); virtual void setPrecision(S32 precision); void setMinValue(F32 min_value) {mMultiSlider->setMinValue(min_value);} @@ -138,7 +138,7 @@ public: virtual void onTabInto(); - virtual void setTentative(BOOL b); // marks value as tentative + virtual void setTentative(bool b); // marks value as tentative virtual void onCommit(); // mark not tentative, then commit virtual void setControlName(const std::string& control_name, LLView* context); @@ -155,8 +155,8 @@ private: private: const LLFontGL* mFont; - BOOL mShowText; - BOOL mCanEditText; + bool mShowText; + bool mCanEditText; S32 mPrecision; LLTextBox* mLabelBox; diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index d736aa6634..239e573f1d 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -216,7 +216,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica ui_inst->mSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name)); } - BOOL show_notification = TRUE; + bool show_notification = true; if (p.ignore.control.isProvided()) { mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control); @@ -1549,7 +1549,7 @@ bool LLNotifications::loadTemplates() std::string base_filename = search_paths.front(); LLXMLNodePtr root; - BOOL success = LLXMLNode::getLayeredXMLNode(root, search_paths); + bool success = LLXMLNode::getLayeredXMLNode(root, search_paths); if (!success || root.isNull() || !root->hasName( "notifications" )) { diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 921398a693..46e1616805 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -443,11 +443,11 @@ public: // return response LLSD filled in with default form contents and (optionally) the default button selected LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON); - // returns index of first button with value==TRUE + // returns index of first button with value==true // usually this the button the user clicked on // returns -1 if no button clicked (e.g. form has not been displayed) static S32 getSelectedOption(const LLSD& notification, const LLSD& response); - // returns name of first button with value==TRUE + // returns name of first button with value==true static std::string getSelectedOptionName(const LLSD& notification); // after someone responds to a notification (usually by clicking a button, diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index f770920c4a..ba6a31eb9e 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -55,7 +55,7 @@ LLPanel::factory_stack_t LLPanel::sFactoryStack; // Compiler optimization, generate extern template template class LLPanel* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; LLPanel::LocalizedString::LocalizedString() : name("name"), @@ -127,9 +127,9 @@ LLPanel::~LLPanel() } // virtual -BOOL LLPanel::isPanel() const +bool LLPanel::isPanel() const { - return TRUE; + return true; } void LLPanel::addBorder(LLViewBorder::Params p) @@ -167,13 +167,13 @@ void LLPanel::clearCtrls() for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it) { LLUICtrl* ctrl = *ctrl_it; - ctrl->setFocus( FALSE ); - ctrl->setEnabled( FALSE ); + ctrl->setFocus( false ); + ctrl->setEnabled( false ); ctrl->clear(); } } -void LLPanel::setCtrlsEnabled( BOOL b ) +void LLPanel::setCtrlsEnabled( bool b ) { LLPanel::ctrl_list_t ctrls = getCtrlList(); for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it) @@ -247,13 +247,13 @@ void LLPanel::updateDefaultBtn() if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled()) { LLButton* buttonp = dynamic_cast(gFocusMgr.getKeyboardFocus()); - BOOL focus_is_child_button = buttonp && buttonp->getCommitOnReturn(); + bool focus_is_child_button = buttonp && buttonp->getCommitOnReturn(); // only enable default button when current focus is not a return-capturing button mDefaultBtn->setBorderEnabled(!focus_is_child_button); } else { - mDefaultBtn->setBorderEnabled(FALSE); + mDefaultBtn->setBorderEnabled(false); } } } @@ -261,19 +261,19 @@ void LLPanel::updateDefaultBtn() void LLPanel::refresh() { // do nothing by default - // but is automatically called in setFocus(TRUE) + // but is automatically called in setFocus(true) } void LLPanel::setDefaultBtn(LLButton* btn) { if (mDefaultBtn && mDefaultBtn->getEnabled()) { - mDefaultBtn->setBorderEnabled(FALSE); + mDefaultBtn->setBorderEnabled(false); } mDefaultBtn = btn; if (mDefaultBtn) { - mDefaultBtn->setBorderEnabled(TRUE); + mDefaultBtn->setBorderEnabled(true); } } @@ -290,17 +290,17 @@ void LLPanel::setDefaultBtn(const std::string& id) } } -BOOL LLPanel::handleKeyHere( KEY key, MASK mask ) +bool LLPanel::handleKeyHere( KEY key, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); // handle user hitting ESC to defocus if (key == KEY_ESCAPE) { - setFocus(FALSE); - return TRUE; + setFocus(false); + return true; } else if( (mask == MASK_SHIFT) && (KEY_TAB == key)) { @@ -310,7 +310,7 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask ) LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); if (focus_root) { - handled = focus_root->focusPrevItem(FALSE); + handled = focus_root->focusPrevItem(false); } } } @@ -322,7 +322,7 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask ) LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); if (focus_root) { - handled = focus_root->focusNextItem(FALSE); + handled = focus_root->focusNextItem(false); } } } @@ -335,38 +335,38 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask ) { // current focus is a return-capturing button, // let *that* button handle the return key - handled = FALSE; + handled = false; } else if (mDefaultBtn && mDefaultBtn->getVisible() && mDefaultBtn->getEnabled()) { // If we have a default button, click it when return is pressed mDefaultBtn->onCommit(); - handled = TRUE; + handled = true; } else if (cur_focus->acceptsTextInput()) { // call onCommit for text input handling control cur_focus->onCommit(); - handled = TRUE; + handled = true; } } return handled; } -void LLPanel::onVisibilityChange ( BOOL new_visibility ) +void LLPanel::onVisibilityChange ( bool new_visibility ) { LLUICtrl::onVisibilityChange ( new_visibility ); if (mVisibleSignal) - (*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass BOOL as LLSD + (*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass bool as LLSD } -void LLPanel::setFocus(BOOL b) +void LLPanel::setFocus(bool b) { if( b && !hasFocus()) { // give ourselves focus preemptively, to avoid infinite loop - LLUICtrl::setFocus(TRUE); + LLUICtrl::setFocus(true); // then try to pass to first valid child focusFirstItem(); } @@ -376,7 +376,7 @@ void LLPanel::setFocus(BOOL b) } } -void LLPanel::setBorderVisible(BOOL b) +void LLPanel::setBorderVisible(bool b) { if (mBorder) { @@ -504,7 +504,7 @@ static LLTrace::BlockTimerStatHandle FTM_PANEL_SETUP("Panel Setup"); static LLTrace::BlockTimerStatHandle FTM_EXTERNAL_PANEL_LOAD("Load Extern Panel Reference"); static LLTrace::BlockTimerStatHandle FTM_PANEL_POSTBUILD("Panel PostBuild"); -BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params) +bool LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params) { Params params(default_params); { @@ -533,7 +533,7 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu setupParamsForExport(output_params, parent); output_node->setName(node->getName()->mString); parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); - return TRUE; + return true; } LLUICtrlFactory::instance().pushFileName(xml_filename); @@ -543,7 +543,7 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu { LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL; - return FALSE; + return false; } parser.readXUI(referenced_xml, params, LLUICtrlFactory::getInstance()->getCurFileName()); @@ -590,7 +590,7 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu postBuild(); } } - return TRUE; + return true; } bool LLPanel::hasString(const std::string& name) @@ -658,7 +658,7 @@ void LLPanel::childSetEnabled(const std::string& id, bool enabled) } } -void LLPanel::childSetFocus(const std::string& id, BOOL focus) +void LLPanel::childSetFocus(const std::string& id, bool focus) { LLUICtrl* child = findChild(id); if (child) @@ -667,7 +667,7 @@ void LLPanel::childSetFocus(const std::string& id, BOOL focus) } } -BOOL LLPanel::childHasFocus(const std::string& id) +bool LLPanel::childHasFocus(const std::string& id) { LLUICtrl* child = findChild(id); if (child) @@ -676,7 +676,7 @@ BOOL LLPanel::childHasFocus(const std::string& id) } else { - return FALSE; + return false; } } @@ -752,24 +752,24 @@ LLSD LLPanel::childGetValue(const std::string& id) const return LLSD(); } -BOOL LLPanel::childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text) +bool LLPanel::childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text) { LLUICtrl* child = findChild(id); if (child) { return child->setTextArg(key, text); } - return FALSE; + return false; } -BOOL LLPanel::childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text) +bool LLPanel::childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text) { LLView* child = findChild(id); if (child) { return child->setLabelArg(key, text); } - return FALSE; + return false; } void LLPanel::childSetAction(const std::string& id, const commit_signal_t::slot_type& function) @@ -803,10 +803,10 @@ boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t:: //----------------------------------------------------------------------------- // buildPanel() //----------------------------------------------------------------------------- -BOOL LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) +bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) { LL_PROFILE_ZONE_SCOPED; - BOOL didPost = FALSE; + bool didPost = false; LLXMLNodePtr root; if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 8018365d3e..33883bf6a4 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -40,8 +40,8 @@ #include const S32 LLPANEL_BORDER_WIDTH = 1; -const BOOL BORDER_YES = TRUE; -const BOOL BORDER_NO = FALSE; +const bool BORDER_YES = true; +const bool BORDER_NO = false; class LLButton; class LLUIImage; @@ -107,20 +107,20 @@ protected: public: typedef std::vector ctrl_list_t; - BOOL buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); + bool buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); static LLPanel* createFactoryPanel(const std::string& name); /*virtual*/ ~LLPanel(); // LLView interface - /*virtual*/ BOOL isPanel() const; + /*virtual*/ bool isPanel() const; /*virtual*/ void draw(); - /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); - /*virtual*/ void onVisibilityChange ( BOOL new_visibility ); + /*virtual*/ bool handleKeyHere( KEY key, MASK mask ); + /*virtual*/ void onVisibilityChange ( bool new_visibility ); // From LLFocusableElement - /*virtual*/ void setFocus( BOOL b ); + /*virtual*/ void setFocus( bool b ); // New virtuals virtual void refresh(); // called in setFocus() @@ -131,8 +131,8 @@ public: void addBorder( LLViewBorder::Params p); void addBorder(); void removeBorder(); - BOOL hasBorder() const { return mBorder != NULL; } - void setBorderVisible( BOOL b ); + bool hasBorder() const { return mBorder != NULL; } + void setBorderVisible( bool b ); void setBackgroundColor( const LLColor4& color ) { mBgOpaqueColor = color; } const LLColor4& getBackgroundColor() const { return mBgOpaqueColor; } @@ -144,10 +144,10 @@ public: LLPointer getTransparentImage() const { return mBgAlphaImage; } LLColor4 getBackgroundImageOverlay() { return mBgOpaqueImageOverlay; } LLColor4 getTransparentImageOverlay() { return mBgAlphaImageOverlay; } - void setBackgroundVisible( BOOL b ) { mBgVisible = b; } - BOOL isBackgroundVisible() const { return mBgVisible; } - void setBackgroundOpaque(BOOL b) { mBgOpaque = b; } - BOOL isBackgroundOpaque() const { return mBgOpaque; } + void setBackgroundVisible( bool b ) { mBgVisible = b; } + bool isBackgroundVisible() const { return mBgVisible; } + void setBackgroundOpaque(bool b) { mBgOpaque = b; } + bool isBackgroundOpaque() const { return mBgOpaque; } void setDefaultBtn(LLButton* btn = NULL); void setDefaultBtn(const std::string& id); void updateDefaultBtn(); @@ -156,7 +156,7 @@ public: void setHelpTopic(const std::string& help_topic) { mHelpTopic = help_topic; } std::string getHelpTopic() const { return mHelpTopic; } - void setCtrlsEnabled(BOOL b); + void setCtrlsEnabled(bool b); ctrl_list_t getCtrlList() const; LLHandle getHandle() const { return getDerivedHandle(); } @@ -167,7 +167,7 @@ public: EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; } void initFromParams(const Params& p); - BOOL initPanelXML( LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params); + bool initPanelXML( LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params); bool hasString(const std::string& name); std::string getString(const std::string& name, const LLStringUtil::format_map_t& args) const; @@ -184,8 +184,8 @@ public: void childDisable(const std::string& name) { childSetEnabled(name, false); }; // LLUICtrl - void childSetFocus(const std::string& id, BOOL focus = TRUE); - BOOL childHasFocus(const std::string& id); + void childSetFocus(const std::string& id, bool focus = true); + bool childHasFocus(const std::string& id); // *TODO: Deprecate; for backwards compatability only: // Prefer getChild("foo")->setCommitCallback(boost:bind(...)), @@ -203,9 +203,9 @@ public: LLSD childGetValue(const std::string& id) const; // For setting text / label replacement params, e.g. "Hello [NAME]" - // Not implemented for all types, defaults to noop, returns FALSE if not applicaple - BOOL childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text); - BOOL childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text); + // Not implemented for all types, defaults to noop, returns false if not applicaple + bool childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text); + bool childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text); // LLButton void childSetAction(const std::string& id, boost::function function, void* value); @@ -238,8 +238,8 @@ protected: std::string mXMLFilename; private: - BOOL mBgVisible; // any background at all? - BOOL mBgOpaque; // use opaque color or image + bool mBgVisible; // any background at all? + bool mBgOpaque; // use opaque color or image LLUIColor mBgOpaqueColor; LLUIColor mBgAlphaColor; LLUIColor mBgOpaqueImageOverlay; @@ -259,7 +259,7 @@ private: // Build time optimization, generate once in .cpp file #ifndef LLPANEL_CPP extern template class LLPanel* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif typedef boost::function LLPanelClassCreatorFunc; diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index 32ce5e5575..04553e1f9f 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -53,7 +53,7 @@ public: /*virtual*/ ~LLRadioCtrl(); /*virtual*/ void setValue(const LLSD& value); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); LLSD getPayload() { return mPayload; } @@ -119,16 +119,16 @@ LLRadioGroup::~LLRadioGroup() } // virtual -BOOL LLRadioGroup::postBuild() +bool LLRadioGroup::postBuild() { if (!mRadioButtons.empty()) { mRadioButtons[0]->setTabStop(true); } - return TRUE; + return true; } -void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) +void LLRadioGroup::setIndexEnabled(S32 index, bool enabled) { S32 count = 0; for (button_list_t::iterator iter = mRadioButtons.begin(); @@ -138,7 +138,7 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) if (count == index) { child->setEnabled(enabled); - if (index == mSelectedIndex && enabled == FALSE) + if (index == mSelectedIndex && enabled == false) { setSelectedIndex(-1); } @@ -173,30 +173,30 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) } } -BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) +bool LLRadioGroup::setSelectedIndex(S32 index, bool from_event) { if ((S32)mRadioButtons.size() <= index ) { - return FALSE; + return false; } if (index < -1) { // less then minimum value - return FALSE; + return false; } if (index < 0 && mSelectedIndex >= 0 && !mAllowDeselect) { // -1 is "nothing selected" - return FALSE; + return false; } if (mSelectedIndex >= 0) { LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex]; old_radio_item->setTabStop(false); - old_radio_item->setValue( FALSE ); + old_radio_item->setValue( false ); } else { @@ -209,11 +209,11 @@ BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) { LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; radio_item->setTabStop(true); - radio_item->setValue( TRUE ); + radio_item->setValue( true ); if (hasFocus()) { - radio_item->focusFirstItem(FALSE, FALSE); + radio_item->focusFirstItem(false, false); } } @@ -222,7 +222,7 @@ BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) setControlValue(getValue()); } - return TRUE; + return true; } void LLRadioGroup::focusSelectedRadioBtn() @@ -232,18 +232,18 @@ void LLRadioGroup::focusSelectedRadioBtn() LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; if (radio_item->hasTabStop() && radio_item->getEnabled()) { - radio_item->focusFirstItem(FALSE, FALSE); + radio_item->focusFirstItem(false, false); } } else if (mRadioButtons[0]->hasTabStop() || hasTabStop()) { - focusFirstItem(FALSE, FALSE); + focusFirstItem(false, false); } } -BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask) +bool LLRadioGroup::handleKeyHere(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // do any of the tab buttons have keyboard focus? if (mask == MASK_NONE) { @@ -258,7 +258,7 @@ BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask) { onCommit(); } - handled = TRUE; + handled = true; break; case KEY_UP: if (!setSelectedIndex((getSelectedIndex() - 1))) @@ -269,7 +269,7 @@ BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask) { onCommit(); } - handled = TRUE; + handled = true; break; case KEY_LEFT: if (!setSelectedIndex((getSelectedIndex() - 1))) @@ -280,7 +280,7 @@ BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask) { onCommit(); } - handled = TRUE; + handled = true; break; case KEY_RIGHT: if (!setSelectedIndex((getSelectedIndex() + 1))) @@ -291,7 +291,7 @@ BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask) { onCommit(); } - handled = TRUE; + handled = true; break; default: break; @@ -358,11 +358,11 @@ void LLRadioGroup::setValue( const LLSD& value ) // string not found, try integer if (value.isInteger()) { - setSelectedIndex((S32) value.asInteger(), TRUE); + setSelectedIndex((S32) value.asInteger(), true); } else { - setSelectedIndex(-1, TRUE); + setSelectedIndex(-1, true); } } } @@ -381,9 +381,9 @@ LLSD LLRadioGroup::getValue() const } // LLCtrlSelectionInterface functions -BOOL LLRadioGroup::setCurrentByID( const LLUUID& id ) +bool LLRadioGroup::setCurrentByID( const LLUUID& id ) { - return FALSE; + return false; } LLUUID LLRadioGroup::getCurrentID() const @@ -391,7 +391,7 @@ LLUUID LLRadioGroup::getCurrentID() const return LLUUID::null; } -BOOL LLRadioGroup::setSelectedByValue(const LLSD& value, BOOL selected) +bool LLRadioGroup::setSelectedByValue(const LLSD& value, bool selected) { S32 idx = 0; for (button_list_t::const_iterator iter = mRadioButtons.begin(); @@ -400,12 +400,12 @@ BOOL LLRadioGroup::setSelectedByValue(const LLSD& value, BOOL selected) if((*iter)->getPayload().asString() == value.asString()) { setSelectedIndex(idx); - return TRUE; + return true; } idx++; } - return FALSE; + return false; } LLSD LLRadioGroup::getSelectedValue() @@ -413,7 +413,7 @@ LLSD LLRadioGroup::getSelectedValue() return getValue(); } -BOOL LLRadioGroup::isSelected(const LLSD& value) const +bool LLRadioGroup::isSelected(const LLSD& value) const { S32 idx = 0; for (button_list_t::const_iterator iter = mRadioButtons.begin(); @@ -423,22 +423,22 @@ BOOL LLRadioGroup::isSelected(const LLSD& value) const { if (idx == mSelectedIndex) { - return TRUE; + return true; } } idx++; } - return FALSE; + return false; } -BOOL LLRadioGroup::operateOnSelection(EOperation op) +bool LLRadioGroup::operateOnSelection(EOperation op) { - return FALSE; + return false; } -BOOL LLRadioGroup::operateOnAll(EOperation op) +bool LLRadioGroup::operateOnAll(EOperation op) { - return FALSE; + return false; } LLRadioGroup::ItemParams::ItemParams() @@ -458,7 +458,7 @@ LLRadioCtrl::LLRadioCtrl(const LLRadioGroup::ItemParams& p) } } -BOOL LLRadioCtrl::postBuild() +bool LLRadioCtrl::postBuild() { // Old-style radio_item used the text contents to indicate the label, // but new-style radio_item uses label attribute. @@ -467,7 +467,7 @@ BOOL LLRadioCtrl::postBuild() { setLabel(value); } - return TRUE; + return true; } bool LLRadioCtrl::handleMouseDown(S32 x, S32 y, MASK mask) diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index dcb2f43bfe..d638605bb3 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -64,15 +64,15 @@ public: virtual ~LLRadioGroup(); - virtual BOOL postBuild(); + virtual bool postBuild(); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); - void setIndexEnabled(S32 index, BOOL enabled); + void setIndexEnabled(S32 index, bool enabled); // return the index value of the selected item S32 getSelectedIndex() const { return mSelectedIndex; } // set the index value programatically - BOOL setSelectedIndex(S32 index, BOOL from_event = FALSE); + bool setSelectedIndex(S32 index, bool from_event = false); // foxus child by index if it can get focus void focusSelectedRadioBtn(); @@ -88,18 +88,18 @@ public: // LLCtrlSelectionInterface functions /*virtual*/ S32 getItemCount() const { return mRadioButtons.size(); } - /*virtual*/ BOOL getCanSelect() const { return TRUE; } - /*virtual*/ BOOL selectFirstItem() { return setSelectedIndex(0); } - /*virtual*/ BOOL selectNthItem( S32 index ) { return setSelectedIndex(index); } - /*virtual*/ BOOL selectItemRange( S32 first, S32 last ) { return setSelectedIndex(first); } + /*virtual*/ bool getCanSelect() const { return true; } + /*virtual*/ bool selectFirstItem() { return setSelectedIndex(0); } + /*virtual*/ bool selectNthItem( S32 index ) { return setSelectedIndex(index); } + /*virtual*/ bool selectItemRange( S32 first, S32 last ) { return setSelectedIndex(first); } /*virtual*/ S32 getFirstSelectedIndex() const { return getSelectedIndex(); } - /*virtual*/ BOOL setCurrentByID( const LLUUID& id ); + /*virtual*/ bool setCurrentByID( const LLUUID& id ); /*virtual*/ LLUUID getCurrentID() const; // LLUUID::null if no items in menu - /*virtual*/ BOOL setSelectedByValue(const LLSD& value, BOOL selected); + /*virtual*/ bool setSelectedByValue(const LLSD& value, bool selected); /*virtual*/ LLSD getSelectedValue(); - /*virtual*/ BOOL isSelected(const LLSD& value) const; - /*virtual*/ BOOL operateOnSelection(EOperation op); - /*virtual*/ BOOL operateOnAll(EOperation op); + /*virtual*/ bool isSelected(const LLSD& value) const; + /*virtual*/ bool operateOnSelection(EOperation op); + /*virtual*/ bool operateOnAll(EOperation op); private: const LLFontGL* mFont; diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h index 71e3ec3094..5c53ddfce6 100644 --- a/indra/llui/llresizebar.h +++ b/indra/llui/llresizebar.h @@ -59,8 +59,8 @@ public: virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); void setResizeLimits( S32 min_size, S32 max_size ) { mMinSize = min_size; mMaxSize = max_size; } - void setEnableSnapping(BOOL enable) { mSnappingEnabled = enable; } - void setAllowDoubleClickSnapping(BOOL allow) { mAllowDoubleClickSnapping = allow; } + void setEnableSnapping(bool enable) { mSnappingEnabled = enable; } + void setAllowDoubleClickSnapping(bool allow) { mAllowDoubleClickSnapping = allow; } bool canResize() { return getEnabled() && mMaxSize > mMinSize; } void setResizeListener(boost::function listener) {mResizeListener = listener;} void setImagePanel(LLPanel * panelp); diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index b1f4a6c69d..760174a878 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -366,7 +366,7 @@ void LLResizeHandle::draw() } -BOOL LLResizeHandle::pointInHandle( S32 x, S32 y ) +bool LLResizeHandle::pointInHandle( S32 x, S32 y ) { if( pointInView(x, y) ) { @@ -378,8 +378,8 @@ BOOL LLResizeHandle::pointInHandle( S32 x, S32 y ) case LEFT_TOP: return (x <= RESIZE_BORDER_WIDTH) || (y >= TOP_BORDER); case LEFT_BOTTOM: return (x <= RESIZE_BORDER_WIDTH) || (y <= RESIZE_BORDER_WIDTH); case RIGHT_TOP: return (x >= RIGHT_BORDER) || (y >= TOP_BORDER); - case RIGHT_BOTTOM: return TRUE; + case RIGHT_BOTTOM: return true; } } - return FALSE; + return false; } diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h index d82934f75b..6dddc42f5e 100644 --- a/indra/llui/llresizehandle.h +++ b/indra/llui/llresizehandle.h @@ -58,7 +58,7 @@ public: void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; } private: - BOOL pointInHandle( S32 x, S32 y ); + bool pointInHandle( S32 x, S32 y ); S32 mDragLastScreenX; S32 mDragLastScreenY; diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp index 23e0842f37..1d166c8bbb 100644 --- a/indra/llui/llresmgr.cpp +++ b/indra/llui/llresmgr.cpp @@ -99,9 +99,9 @@ std::string LLResMgr::getMonetaryString( S32 input ) const // Note: we assume here that the currency symbol goes on the left. (Hey, it's Lindens! We can just decide.) - BOOL negative = (input < 0 ); - BOOL negative_before = negative && (conv->n_sign_posn != 2); - BOOL negative_after = negative && (conv->n_sign_posn == 2); + bool negative = (input < 0 ); + bool negative_before = negative && (conv->n_sign_posn != 2); + bool negative_after = negative && (conv->n_sign_posn == 2); std::string digits = llformat("%u", abs(input)); if( !grouping || !grouping[0] ) diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 2cc0644d90..f520ba2fb8 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -73,7 +73,7 @@ LLScrollbar::LLScrollbar(const Params & p) mDocPos( p.doc_pos ), mPageSize( p.page_size ), mStepSize( p.step_size ), - mDocChanged(FALSE), + mDocChanged(false), mDragStartX( 0 ), mDragStartY( 0 ), mHoverGlowStrength(0.15f), @@ -136,19 +136,19 @@ void LLScrollbar::setDocParams( S32 size, S32 pos ) { mDocSize = size; setDocPos(pos); - mDocChanged = TRUE; + mDocChanged = true; updateThumbRect(); } // returns true if document position really changed -bool LLScrollbar::setDocPos(S32 pos, BOOL update_thumb) +bool LLScrollbar::setDocPos(S32 pos, bool update_thumb) { pos = llclamp(pos, 0, getDocPosMax()); if (pos != mDocPos) { mDocPos = pos; - mDocChanged = TRUE; + mDocChanged = true; if( mChangeCallback ) { @@ -170,7 +170,7 @@ void LLScrollbar::setDocSize(S32 size) { mDocSize = size; setDocPos(mDocPos); - mDocChanged = TRUE; + mDocChanged = true; updateThumbRect(); } @@ -182,7 +182,7 @@ void LLScrollbar::setPageSize( S32 page_size ) { mPageSize = page_size; setDocPos(mDocPos); - mDocChanged = TRUE; + mDocChanged = true; updateThumbRect(); } @@ -329,7 +329,7 @@ bool LLScrollbar::handleHover(S32 x, S32 y, MASK mask) 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 // out of sync (less than a line's worth) to make the thumb feel responsive. - changeLine( new_pos - mDocPos, FALSE ); + changeLine( new_pos - mDocPos, false ); } } @@ -373,7 +373,7 @@ bool LLScrollbar::handleHover(S32 x, S32 y, MASK mask) // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly // out of sync (less than a line's worth) to make the thumb feel responsive. - changeLine( new_pos - mDocPos, FALSE ); + changeLine( new_pos - mDocPos, false ); } } @@ -410,7 +410,7 @@ bool LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) bool LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks) { - bool handled = FALSE; + bool handled = false; if (LLScrollbar::HORIZONTAL == mOrientation) { handled = changeLine(clicks * mStepSize, true); @@ -418,7 +418,7 @@ bool LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks) return handled; } -BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +bool LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string &tooltip_msg) { // enable this to get drag and drop to control scrollbars @@ -434,10 +434,10 @@ BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // : F32(pos - SCROLLBAR_SIZE) / usable_track_length; // S32 new_pos = (VERTICAL == mOrientation) ? llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines ) // : llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines ); - // changeLine( new_pos - mDocPos, TRUE ); + // changeLine( new_pos - mDocPos, true ); //} - //return TRUE; - return FALSE; + //return true; + return false; } bool LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask) @@ -464,7 +464,7 @@ bool LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask) } -void LLScrollbar::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLScrollbar::reshape(S32 width, S32 height, bool called_from_parent) { if (width == getRect().getWidth() && height == getRect().getHeight()) return; LLView::reshape( width, height, called_from_parent ); @@ -493,14 +493,14 @@ void LLScrollbar::draw() if(mBGVisible) { - gl_rect_2d(getLocalRect(), mBGColor.get(), TRUE); + gl_rect_2d(getLocalRect(), mBGColor.get(), true); } S32 local_mouse_x; S32 local_mouse_y; LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); - BOOL other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this; - BOOL hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y)); + bool other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this; + bool hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y)); if (hovered) { mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLSmoothInterpolation::getInterpolant(0.05f)); @@ -517,9 +517,9 @@ void LLScrollbar::draw() 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.get(), TRUE); + mOrientation == VERTICAL ? mThickness : 0, mTrackColor.get(), true); - gl_rect_2d(mThumbRect, mThumbColor.get(), TRUE); + gl_rect_2d(mThumbRect, mThumbColor.get(), true); } else @@ -578,7 +578,7 @@ void LLScrollbar::draw() } // end draw -bool LLScrollbar::changeLine( S32 delta, BOOL update_thumb ) +bool LLScrollbar::changeLine( S32 delta, bool update_thumb ) { return setDocPos(mDocPos + delta, update_thumb); } @@ -589,35 +589,35 @@ void LLScrollbar::setValue(const LLSD& value) } -BOOL LLScrollbar::handleKeyHere(KEY key, MASK mask) +bool LLScrollbar::handleKeyHere(KEY key, MASK mask) { if (getDocPosMax() == 0 && !getVisible()) { - return FALSE; + return false; } - BOOL handled = FALSE; + bool handled = false; switch( key ) { case KEY_HOME: setDocPos( 0 ); - handled = TRUE; + handled = true; break; case KEY_END: setDocPos( getDocPosMax() ); - handled = TRUE; + handled = true; break; case KEY_DOWN: setDocPos( getDocPos() + mStepSize ); - handled = TRUE; + handled = true; break; case KEY_UP: setDocPos( getDocPos() - mStepSize ); - handled = TRUE; + handled = true; break; case KEY_PAGE_DOWN: @@ -636,7 +636,7 @@ void LLScrollbar::pageUp(S32 overlap) { if (mDocSize > mPageSize) { - changeLine( -(mPageSize - overlap), TRUE ); + changeLine( -(mPageSize - overlap), true ); } } @@ -644,18 +644,18 @@ void LLScrollbar::pageDown(S32 overlap) { if (mDocSize > mPageSize) { - changeLine( mPageSize - overlap, TRUE ); + changeLine( mPageSize - overlap, true ); } } void LLScrollbar::onLineUpBtnPressed( const LLSD& data ) { - changeLine( -mStepSize, TRUE ); + changeLine( -mStepSize, true ); } void LLScrollbar::onLineDownBtnPressed( const LLSD& data ) { - changeLine( mStepSize, TRUE ); + changeLine( mStepSize, true ); } void LLScrollbar::setThickness(S32 thickness) diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 82607f2dd0..b381c57283 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -82,17 +82,17 @@ public: virtual void setValue(const LLSD& value); // Overrides from LLView - virtual BOOL handleKeyHere(KEY key, MASK mask); + 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 handleDoubleClick(S32 x, S32 y, MASK mask); virtual bool handleHover(S32 x, S32 y, MASK mask); virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); virtual bool handleScrollHWheel(S32 x, S32 y, S32 clicks); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + 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 reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); virtual void draw(); @@ -102,7 +102,7 @@ public: // How many "lines" the "document" has scrolled. // 0 <= DocPos <= DocSize - DocVisibile - bool setDocPos( S32 pos, BOOL update_thumb = TRUE ); + bool setDocPos( S32 pos, bool update_thumb = true ); S32 getDocPos() const { return mDocPos; } bool isAtBeginning() const; @@ -129,7 +129,7 @@ public: private: void updateThumbRect(); - bool changeLine(S32 delta, BOOL update_thumb ); + bool changeLine(S32 delta, bool update_thumb ); callback_t mChangeCallback; @@ -138,7 +138,7 @@ private: S32 mDocPos; // Position within the doc that the scrollbar is modeling, in "lines" (user size) S32 mPageSize; // Maximum number of lines that can be seen at one time. S32 mStepSize; - BOOL mDocChanged; + bool mDocChanged; LLRect mThumbRect; S32 mDragStartX; diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 48b55a1d87..09e8fd1f84 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -81,7 +81,7 @@ LLScrollContainer::Params::Params() // Default constructor LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) : LLUICtrl(p), - mAutoScrolling( FALSE ), + mAutoScrolling( false ), mAutoScrollRate( 0.f ), mBackgroundColor(p.bg_color()), mIsOpaque(p.is_opaque), @@ -178,7 +178,7 @@ void LLScrollContainer::scrollVertical( S32 new_pos ) // LLView functionality void LLScrollContainer::reshape(S32 width, S32 height, - BOOL called_from_parent) + bool called_from_parent) { LLUICtrl::reshape( width, height, called_from_parent ); @@ -191,8 +191,8 @@ void LLScrollContainer::reshape(S32 width, S32 height, S32 visible_width = 0; S32 visible_height = 0; - BOOL show_v_scrollbar = FALSE; - BOOL show_h_scrollbar = FALSE; + bool show_v_scrollbar = false; + bool show_h_scrollbar = false; calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); @@ -204,7 +204,7 @@ void LLScrollContainer::reshape(S32 width, S32 height, } } -BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask) +bool LLScrollContainer::handleKeyHere(KEY key, MASK mask) { // allow scrolled view to handle keystrokes in case it delegated keyboard focus // to the scroll container. @@ -213,18 +213,18 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask) // call LLScrollContainer::handleKeyHere in turn if (mScrolledView && mScrolledView->handleKeyHere(key, mask)) { - return TRUE; + return true; } for( S32 i = 0; i < ORIENTATION_COUNT; i++ ) { if( mScrollbar[i]->handleKeyHere(key, mask) ) { updateScroll(); - return TRUE; + return true; } } - return FALSE; + return false; } bool LLScrollContainer::handleUnicodeCharHere(llwchar uni_char) @@ -292,8 +292,8 @@ bool LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks) return false; } -BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, +bool LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -301,7 +301,7 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, { // Scroll folder view if needed. Never accepts a drag or drop. *accept = ACCEPT_NO; - BOOL handled = autoScroll(x, y); + bool handled = autoScroll(x, y); if( !handled ) { @@ -309,7 +309,7 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, cargo_data, accept, tooltip_msg) != NULL; } - return TRUE; + return true; } bool LLScrollContainer::canAutoScroll(S32 x, S32 y) @@ -366,7 +366,7 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll) if (do_scroll) { mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed); - mAutoScrolling = TRUE; + mAutoScrolling = true; } scrolling = true; } @@ -378,7 +378,7 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll) if (do_scroll) { mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed); - mAutoScrolling = TRUE; + mAutoScrolling = true; } scrolling = true; } @@ -392,7 +392,7 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll) if (do_scroll) { mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed); - mAutoScrolling = TRUE; + mAutoScrolling = true; } scrolling = true; } @@ -404,7 +404,7 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll) if (do_scroll) { mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed); - mAutoScrolling = TRUE; + mAutoScrolling = true; } scrolling = true; } @@ -413,7 +413,7 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll) return scrolling; } -void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const +void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const { const LLRect& doc_rect = getScrolledViewRect(); static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); @@ -426,8 +426,8 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height *visible_width = getRect().getWidth() - 2 * border_width; *visible_height = getRect().getHeight() - 2 * border_width; - *show_v_scrollbar = FALSE; - *show_h_scrollbar = FALSE; + *show_v_scrollbar = false; + *show_h_scrollbar = false; if (!mHideScrollbar) { @@ -435,12 +435,12 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height // the display of sliders. if ((doc_height - *visible_height) > 1) { - *show_v_scrollbar = TRUE; + *show_v_scrollbar = true; *visible_width -= scrollbar_size; } if ((doc_width - *visible_width) > 1) { - *show_h_scrollbar = TRUE; + *show_h_scrollbar = true; *visible_height -= scrollbar_size; // Note: Do *not* recompute *show_v_scrollbar here because with // The view inside the scroll container should not be extended @@ -449,7 +449,7 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) ) { - *show_v_scrollbar = TRUE; + *show_v_scrollbar = true; *visible_width -= scrollbar_size; } } @@ -473,7 +473,7 @@ void LLScrollContainer::draw() mAutoScrollRate = mMinAutoScrollRate; } // clear this flag to be set on next call to autoScroll - mAutoScrolling = FALSE; + mAutoScrolling = false; // auto-focus when scrollbar active // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc) @@ -506,8 +506,8 @@ void LLScrollContainer::draw() { S32 visible_width = 0; S32 visible_height = 0; - BOOL show_v_scrollbar = FALSE; - BOOL show_h_scrollbar = FALSE; + 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, @@ -578,8 +578,8 @@ void LLScrollContainer::updateScroll() S32 doc_height = doc_rect.getHeight(); S32 visible_width = 0; S32 visible_height = 0; - BOOL show_v_scrollbar = FALSE; - BOOL show_h_scrollbar = FALSE; + bool show_v_scrollbar = false; + bool show_h_scrollbar = false; calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); S32 border_width = getBorderWidth(); @@ -591,14 +591,14 @@ void LLScrollContainer::updateScroll() } scrollVertical( mScrollbar[VERTICAL]->getDocPos() ); - mScrollbar[VERTICAL]->setVisible( TRUE ); + mScrollbar[VERTICAL]->setVisible( true ); S32 v_scrollbar_height = visible_height; if( !show_h_scrollbar && mReserveScrollCorner ) { 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; @@ -614,7 +614,7 @@ void LLScrollContainer::updateScroll() { mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop ); - mScrollbar[VERTICAL]->setVisible( FALSE ); + mScrollbar[VERTICAL]->setVisible( false ); mScrollbar[VERTICAL]->setDocPos( 0 ); } @@ -630,19 +630,19 @@ void LLScrollContainer::updateScroll() scrollHorizontal( mScrollbar[HORIZONTAL]->getDocPos() ); } - mScrollbar[HORIZONTAL]->setVisible( TRUE ); + mScrollbar[HORIZONTAL]->setVisible( true ); S32 h_scrollbar_width = visible_width; if( !show_v_scrollbar && mReserveScrollCorner ) { h_scrollbar_width -= scrollbar_size; } - mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, scrollbar_size, TRUE ); + mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, scrollbar_size, true ); } else { mScrolledView->translate( border_width - doc_rect.mLeft, 0 ); - mScrollbar[HORIZONTAL]->setVisible( FALSE ); + mScrollbar[HORIZONTAL]->setVisible( false ); mScrollbar[HORIZONTAL]->setDocPos( 0 ); } @@ -653,7 +653,7 @@ void LLScrollContainer::updateScroll() mScrollbar[VERTICAL]->setPageSize( visible_height ); } // end updateScroll -void LLScrollContainer::setBorderVisible(BOOL b) +void LLScrollContainer::setBorderVisible(bool b) { mBorder->setVisible( b ); // Recompute inner rect, as border visibility changes it @@ -676,8 +676,8 @@ LLRect LLScrollContainer::getContentWindowRect() LLRect scroller_view_rect; S32 visible_width = 0; S32 visible_height = 0; - BOOL show_h_scrollbar = FALSE; - BOOL show_v_scrollbar = FALSE; + 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, diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index 2875f95526..13f69cb83b 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -85,12 +85,12 @@ public: virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); } - void setBorderVisible( BOOL b ); + void setBorderVisible( bool b ); 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; } + void setReserveScrollCorner( bool b ) { mReserveScrollCorner = b; } LLRect getVisibleContentRect(); LLRect getContentWindowRect(); virtual const LLRect getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; } @@ -105,12 +105,12 @@ public: S32 getBorderWidth() const; // LLView functionality - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + virtual bool handleKeyHere(KEY key, MASK mask); virtual bool handleUnicodeCharHere(llwchar uni_char); virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); virtual bool handleScrollHWheel( S32 x, S32 y, S32 clicks ); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -134,16 +134,16 @@ private: virtual void scrollVertical( S32 new_pos ); void updateScroll(); bool autoScroll(S32 x, S32 y, bool do_scroll); - void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const; + void calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const; LLScrollbar* mScrollbar[ORIENTATION_COUNT]; S32 mSize; - BOOL mIsOpaque; + bool mIsOpaque; LLUIColor mBackgroundColor; LLRect mInnerRect; LLViewBorder* mBorder; - BOOL mReserveScrollCorner; - BOOL mAutoScrolling; + bool mReserveScrollCorner; + bool mAutoScrolling; F32 mAutoScrollRate; F32 mMinAutoScrollRate; F32 mMaxAutoScrollRate; diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index b6f2eb8ba2..e16ba9627a 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -152,7 +152,7 @@ void LLScrollingPanelList::removePanel( U32 panel_index ) } } -void LLScrollingPanelList::updatePanels(BOOL allow_modify) +void LLScrollingPanelList::updatePanels(bool allow_modify) { for (std::deque::iterator iter = mPanelList.begin(); iter != mPanelList.end(); ++iter) @@ -191,7 +191,7 @@ void LLScrollingPanelList::updatePanelVisiblilty() local_rect.getWidth(), local_rect.getHeight(), &screen_rect.mRight, &screen_rect.mTop ); - BOOL intersects = + bool intersects = ( (screen_rect.mRight > parent_screen_rect.mLeft) && (screen_rect.mLeft < parent_screen_rect.mRight) ) && ( (screen_rect.mTop > parent_screen_rect.mBottom) && (screen_rect.mBottom < parent_screen_rect.mTop) ); diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h index e8df176ec3..964fa1ba40 100644 --- a/indra/llui/llscrollingpanellist.h +++ b/indra/llui/llscrollingpanellist.h @@ -40,7 +40,7 @@ class LLScrollingPanel : public LLPanel { public: LLScrollingPanel(const LLPanel::Params& params) : LLPanel(params) {} - virtual void updatePanel(BOOL allow_modify) = 0; + virtual void updatePanel(bool allow_modify) = 0; }; @@ -68,7 +68,7 @@ public: S32 addPanel( LLScrollingPanel* panel ); void removePanel( LLScrollingPanel* panel ); void removePanel( U32 panel_index ); - void updatePanels(BOOL allow_modify); + void updatePanels(bool allow_modify); const panel_list_t& getPanelList() { return mPanelList; } private: diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index f73c9aa539..89435b1874 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -281,9 +281,9 @@ void LLScrollListText::highlightText(S32 offset, S32 num_chars) } //virtual -BOOL LLScrollListText::isText() const +bool LLScrollListText::isText() const { - return TRUE; + return true; } // virtual @@ -298,7 +298,7 @@ const std::string &LLScrollListText::getToolTip() const } // virtual -BOOL LLScrollListText::needsToolTip() const +bool LLScrollListText::needsToolTip() const { // If base class has a tooltip, return that if (LLScrollListCell::needsToolTip()) @@ -309,7 +309,7 @@ BOOL LLScrollListText::needsToolTip() const } //virtual -BOOL LLScrollListText::getVisible() const +bool LLScrollListText::getVisible() const { return mVisible; } @@ -335,7 +335,7 @@ S32 LLScrollListText::getContentWidth() const void LLScrollListText::setColor(const LLColor4& color) { mColor = color; - mUseColor = TRUE; + mUseColor = true; } void LLScrollListText::setText(const LLStringExplicit& text) @@ -436,7 +436,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col string_chars, getTextWidth(), &right_x, - TRUE); + true); } // @@ -480,14 +480,14 @@ void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_co mCheckBox->draw(); } -BOOL LLScrollListCheck::handleClick() +bool LLScrollListCheck::handleClick() { if (mCheckBox->getEnabled()) { mCheckBox->toggle(); } // don't change selection when clicking on embedded checkbox - return TRUE; + return true; } /*virtual*/ @@ -509,7 +509,7 @@ void LLScrollListCheck::onCommit() } /*virtual*/ -void LLScrollListCheck::setEnabled(BOOL enable) +void LLScrollListCheck::setEnabled(bool enable) { mCheckBox->setEnabled(enable); } @@ -660,7 +660,7 @@ void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight string_chars, getTextWidth(), &right_x, - TRUE); + true); if (mIcon) { diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h index 2588da2331..38184e7860 100644 --- a/indra/llui/llscrolllistcell.h +++ b/indra/llui/llscrolllistcell.h @@ -106,16 +106,16 @@ public: virtual void setAltValue(const LLSD& value) { } virtual const std::string &getToolTip() const { return mToolTip; } virtual void setToolTip(const std::string &str) { mToolTip = str; } - virtual BOOL getVisible() const { return TRUE; } + virtual bool getVisible() const { return true; } virtual void setWidth(S32 width) { mWidth = width; } virtual void highlightText(S32 offset, S32 num_chars) {} - virtual BOOL isText() const { return FALSE; } - virtual BOOL needsToolTip() const { return ! mToolTip.empty(); } + virtual bool isText() const { return false; } + virtual bool needsToolTip() const { return ! mToolTip.empty(); } virtual void setColor(const LLColor4&) {} virtual void onCommit() {}; - virtual BOOL handleClick() { return FALSE; } - virtual void setEnabled(BOOL enable) { } + virtual bool handleClick() { return false; } + virtual void setEnabled(bool enable) { } private: S32 mWidth; @@ -146,13 +146,13 @@ public: /*virtual*/ void setAltValue(const LLSD& value); /*virtual*/ const LLSD getValue() const; /*virtual*/ const LLSD getAltValue() const; - /*virtual*/ BOOL getVisible() const; + /*virtual*/ bool getVisible() const; /*virtual*/ void highlightText(S32 offset, S32 num_chars); /*virtual*/ void setColor(const LLColor4&); - /*virtual*/ BOOL isText() const; + /*virtual*/ bool isText() const; /*virtual*/ const std::string & getToolTip() const; - /*virtual*/ BOOL needsToolTip() const; + /*virtual*/ bool needsToolTip() const; S32 getTextWidth() const { return mTextWidth;} void setTextWidth(S32 value) { mTextWidth = value;} @@ -171,7 +171,7 @@ protected: LLColor4 mHighlightColor; U8 mUseColor; LLFontGL::HAlign mFontAlignment; - BOOL mVisible; + bool mVisible; S32 mHighlightCount; S32 mHighlightOffset; @@ -235,8 +235,8 @@ public: /*virtual*/ void setValue(const LLSD& value); /*virtual*/ void onCommit(); - /*virtual*/ BOOL handleClick(); - /*virtual*/ void setEnabled(BOOL enable); + /*virtual*/ bool handleClick(); + /*virtual*/ void setEnabled(bool enable); LLCheckBoxCtrl* getCheckBox() { return mCheckBox; } diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp index 3dff93af98..a935ea2f11 100644 --- a/indra/llui/llscrolllistcolumn.cpp +++ b/indra/llui/llscrolllistcolumn.cpp @@ -52,7 +52,7 @@ LLScrollColumnHeader::Params::Params() LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p) : LLButton(p), // use combobox params to steal images mColumn(p.column), - mHasResizableElement(FALSE) + mHasResizableElement(false) { setClickedCallback(boost::bind(&LLScrollColumnHeader::onClick, this, _2)); @@ -74,12 +74,12 @@ LLScrollColumnHeader::~LLScrollColumnHeader() void LLScrollColumnHeader::draw() { std::string sort_column = mColumn->mParentCtrl->getSortColumnName(); - BOOL draw_arrow = !mColumn->mLabel.empty() + bool draw_arrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() // check for indirect sorting column as well as column's sorting name && (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName); - BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); + bool is_ascending = mColumn->mParentCtrl->getSortAscending(); if (draw_arrow) { setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white); @@ -241,7 +241,7 @@ void LLScrollColumnHeader::handleReshape(const LLRect& new_rect, bool by_user) } } -void LLScrollColumnHeader::setHasResizableElement(BOOL resizable) +void LLScrollColumnHeader::setHasResizableElement(bool resizable) { if (mHasResizableElement != resizable) { @@ -270,7 +270,7 @@ void LLScrollColumnHeader::updateResizeBars() { LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); if (!columnp || !columnp->mHeader) continue; - BOOL enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize(); + bool enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize(); columnp->mHeader->enableResizeBar(enable); if (enable) { @@ -279,12 +279,12 @@ void LLScrollColumnHeader::updateResizeBars() } } -void LLScrollColumnHeader::enableResizeBar(BOOL enable) +void LLScrollColumnHeader::enableResizeBar(bool enable) { mResizeBar->setEnabled(enable); } -BOOL LLScrollColumnHeader::canResize() +bool LLScrollColumnHeader::canResize() { return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth); } diff --git a/indra/llui/llscrolllistcolumn.h b/indra/llui/llscrolllistcolumn.h index 1699c3d4e9..630e5ef529 100644 --- a/indra/llui/llscrolllistcolumn.h +++ b/indra/llui/llscrolllistcolumn.h @@ -56,17 +56,17 @@ public: /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); LLScrollListColumn* getColumn() { return mColumn; } - void setHasResizableElement(BOOL resizable); + void setHasResizableElement(bool resizable); void updateResizeBars(); - BOOL canResize(); - void enableResizeBar(BOOL enable); + bool canResize(); + void enableResizeBar(bool enable); void onClick(const LLSD& data); private: LLScrollListColumn* mColumn; LLResizeBar* mResizeBar; - BOOL mHasResizableElement; + bool mHasResizableElement; }; /* @@ -158,7 +158,7 @@ public: ESortDirection mSortDirection; LLUIString mLabel; F32 mRelWidth; - BOOL mDynamicWidth; + bool mDynamicWidth; S32 mMaxContentWidth; S32 mIndex; LLScrollListCtrl* mParentCtrl; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 56873ec11a..251ac46e2f 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -66,7 +66,7 @@ static LLDefaultChildRegistry::Register r("scroll_list"); // local structures & classes. struct SortScrollListItem { - SortScrollListItem(const std::vector >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal, bool alternate_sort) + SortScrollListItem(const std::vector >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal, bool alternate_sort) : mSortOrders(sort_orders) , mSortSignal(sort_signal) , mAltSort(alternate_sort) @@ -80,7 +80,7 @@ struct SortScrollListItem it != mSortOrders.rend(); ++it) { S32 col_idx = it->first; - BOOL sort_ascending = it->second; + bool sort_ascending = it->second; S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column? @@ -114,7 +114,7 @@ struct SortScrollListItem } - typedef std::vector > sort_order_t; + typedef std::vector > sort_order_t; const LLScrollListCtrl::sort_signal_t* mSortSignal; const sort_order_t& mSortOrders; const bool mAltSort; @@ -357,7 +357,7 @@ LLScrollListCtrl::~LLScrollListCtrl() } -BOOL LLScrollListCtrl::setMaxItemCount(S32 max_count) +bool LLScrollListCtrl::setMaxItemCount(S32 max_count) { if (max_count >= getItemCount()) { @@ -376,7 +376,7 @@ S32 LLScrollListCtrl::getItemCount() const return mItemList.size(); } -BOOL LLScrollListCtrl::hasSelectedItem() const +bool LLScrollListCtrl::hasSelectedItem() const { item_list::iterator iter; for (iter = mItemList.begin(); iter < mItemList.end(); ) @@ -384,11 +384,11 @@ BOOL LLScrollListCtrl::hasSelectedItem() const LLScrollListItem* itemp = *iter; if (itemp && itemp->getSelected()) { - return TRUE; + return true; } iter++; } - return FALSE; + return false; } // virtual LLScrolListInterface function (was deleteAllItems) @@ -523,7 +523,7 @@ LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const } -void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) +void LLScrollListCtrl::reshape( S32 width, S32 height, bool called_from_parent ) { LLUICtrl::reshape( width, height, called_from_parent ); @@ -551,7 +551,7 @@ void LLScrollListCtrl::updateLayout() // how many lines of content in a single "page" S32 page_lines = getLinesPerPage(); - BOOL scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight(); + bool scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight(); if (scrollbar_visible) { // provide space on the right for scrollbar @@ -593,9 +593,9 @@ LLRect LLScrollListCtrl::getRequiredRect() } -BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL requires_column ) +bool LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, bool requires_column ) { - BOOL not_too_big = getItemCount() < mMaxItemCount; + bool not_too_big = getItemCount() < mMaxItemCount; if (not_too_big) { switch( pos ) @@ -851,12 +851,12 @@ void LLScrollListCtrl::setPageLines(S32 new_page_lines) updateLayout(); } -BOOL LLScrollListCtrl::selectFirstItem() +bool LLScrollListCtrl::selectFirstItem() { - BOOL success = FALSE; + bool success = false; // our $%&@#$()^%#$()*^ iterators don't let us check against the first item inside out iteration - BOOL first_item = TRUE; + bool first_item = true; item_list::iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -876,7 +876,7 @@ BOOL LLScrollListCtrl::selectFirstItem() selectItem(itemp, -1); } } - success = TRUE; + success = true; mOriginalSelection = 0; } else @@ -894,17 +894,17 @@ BOOL LLScrollListCtrl::selectFirstItem() // Deselects all other items // virtual -BOOL LLScrollListCtrl::selectNthItem( S32 target_index ) +bool LLScrollListCtrl::selectNthItem( S32 target_index ) { return selectItemRange(target_index, target_index); } // virtual -BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) +bool LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) { if (mItemList.empty()) { - return FALSE; + return false; } // make sure sort is up to date @@ -918,7 +918,7 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) else last_index = llclamp(last_index, first_index, listlen-1); - BOOL success = FALSE; + bool success = false; S32 index = 0; for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ) { @@ -934,8 +934,8 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) if( itemp->getEnabled() ) { // TODO: support range selection for cells - selectItem(itemp, -1, FALSE); - success = TRUE; + selectItem(itemp, -1, false); + success = true; } } else @@ -1083,7 +1083,7 @@ S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) if (item->getEnabled() && (item->getUUID() == (*iditr))) { // TODO: support multiple selection for cells - selectItem(item, -1, FALSE); + selectItem(item, -1, false); ++count; break; } @@ -1134,7 +1134,7 @@ S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const return -1; } -void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) +void LLScrollListCtrl::selectPrevItem( bool extend_selection) { LLScrollListItem* prev_item = NULL; @@ -1179,7 +1179,7 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) } -void LLScrollListCtrl::selectNextItem( BOOL extend_selection) +void LLScrollListCtrl::selectNextItem( bool extend_selection) { LLScrollListItem* next_item = NULL; @@ -1224,7 +1224,7 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection) -void LLScrollListCtrl::deselectAllItems(BOOL no_commit_on_change) +void LLScrollListCtrl::deselectAllItems(bool no_commit_on_change) { item_list::iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -1263,9 +1263,9 @@ LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos) // Selects first enabled item of the given name. // Returns false if item not found. // Calls getItemByLabel in order to combine functionality -BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sensitive, S32 column/* = 0*/) +bool LLScrollListCtrl::selectItemByLabel(const std::string& label, bool case_sensitive, S32 column/* = 0*/) { - deselectAllItems(TRUE); // ensure that no stale items are selected, even if we don't find a match + deselectAllItems(true); // ensure that no stale items are selected, even if we don't find a match LLScrollListItem* item = getItemByLabel(label, case_sensitive, column); bool found = NULL != item; @@ -1282,7 +1282,7 @@ BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sen return found; } -LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, BOOL case_sensitive, S32 column) +LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, bool case_sensitive, S32 column) { if (label.empty()) //RN: assume no empty items { @@ -1313,16 +1313,16 @@ LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, BOO } -BOOL LLScrollListCtrl::selectItemByPrefix(const std::string& target, BOOL case_sensitive, S32 column) +bool LLScrollListCtrl::selectItemByPrefix(const std::string& target, bool case_sensitive, S32 column) { return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive, column); } // Selects first enabled item that has a name where the name's first part matched the target string. // Returns false if item not found. -BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sensitive, S32 column) +bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target, bool case_sensitive, S32 column) { - BOOL found = FALSE; + bool found = false; LLWString target_trimmed( target ); S32 target_len = target_trimmed.size(); @@ -1336,11 +1336,11 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen LLScrollListItem* item = *iter; // Only select enabled items with matching names LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column); - BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE; + bool select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : false; if (select) { selectItem(item, -1); - found = TRUE; + found = true; break; } } @@ -1372,7 +1372,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen LLWString trimmed_label = item_label; LLWStringUtil::trim(trimmed_label); - BOOL select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0; + bool select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0; if (select) { @@ -1380,7 +1380,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen S32 offset = item_label.find(target_trimmed); cellp->highlightText(offset, target_trimmed.size()); selectItem(item, -1); - found = TRUE; + found = true; break; } } @@ -1413,7 +1413,7 @@ U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitiv } else { - deselectAllItems(TRUE); + deselectAllItems(true); if (!case_sensitive) { // do comparisons in lower case @@ -1447,7 +1447,7 @@ U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitiv { // find offset of matching text cellp->highlightText(found_iter, substring_trimmed.size()); - selectItem(item, -1, FALSE); + selectItem(item, -1, false); found++; @@ -1489,7 +1489,7 @@ const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which // has an associated, unique UUID, and only one of which can be selected at a time. -LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled) +LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, bool enabled) { if (getItemCount() < mMaxItemCount) { @@ -1504,16 +1504,16 @@ LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_te } // Select the line or lines that match this UUID -BOOL LLScrollListCtrl::selectByID( const LLUUID& id ) +bool LLScrollListCtrl::selectByID( const LLUUID& id ) { return selectByValue( LLSD(id) ); } -BOOL LLScrollListCtrl::setSelectedByValue(const LLSD& value, BOOL selected) +bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected) { - BOOL found = FALSE; + bool found = false; - if (selected && !mAllowMultipleSelection) deselectAllItems(TRUE); + if (selected && !mAllowMultipleSelection) deselectAllItems(true); item_list::iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -1527,12 +1527,12 @@ BOOL LLScrollListCtrl::setSelectedByValue(const LLSD& value, BOOL selected) { LLSD::Binary data1 = value.asBinary(); LLSD::Binary data2 = item->getValue().asBinary(); - found = std::equal(data1.begin(), data1.end(), data2.begin()) ? TRUE : FALSE; + found = std::equal(data1.begin(), data1.end(), data2.begin()) ? true : false; } } else { - found = item->getValue().asString() == value.asString() ? TRUE : FALSE; + found = item->getValue().asString() == value.asString() ? true : false; } if (found) @@ -1558,7 +1558,7 @@ BOOL LLScrollListCtrl::setSelectedByValue(const LLSD& value, BOOL selected) return found; } -BOOL LLScrollListCtrl::isSelected(const LLSD& value) const +bool LLScrollListCtrl::isSelected(const LLSD& value) const { item_list::const_iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -1569,7 +1569,7 @@ BOOL LLScrollListCtrl::isSelected(const LLSD& value) const return item->getSelected(); } } - return FALSE; + return false; } LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() const @@ -1736,7 +1736,7 @@ void LLScrollListCtrl::draw() LLUICtrl::draw(); } -void LLScrollListCtrl::setEnabled(BOOL enabled) +void LLScrollListCtrl::setEnabled(bool enabled) { mCanSelect = enabled; setTabStop(enabled); @@ -1828,11 +1828,11 @@ bool LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) return handled; } -BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) +bool LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { - if (!mCanSelect) return FALSE; + if (!mCanSelect) return false; - BOOL selection_changed = FALSE; + bool selection_changed = false; LLScrollListItem* hit_item = hitItem(x, y); @@ -1868,17 +1868,17 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) LLScrollListItem *item = *itor; if (item == hit_item || item == lastSelected) { - selectItem(item, getColumnIndexFromOffset(x), FALSE); + selectItem(item, getColumnIndexFromOffset(x), false); selecting = !selecting; if (hit_item == lastSelected) { // stop selecting now, since we just clicked on our last selected item - selecting = FALSE; + selecting = false; } } if (selecting) { - selectItem(item, getColumnIndexFromOffset(x), FALSE); + selectItem(item, getColumnIndexFromOffset(x), false); } } } @@ -1893,7 +1893,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) { - selectItem(hit_item, getColumnIndexFromOffset(x), FALSE); + selectItem(hit_item, getColumnIndexFromOffset(x), false); } else { @@ -1906,7 +1906,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) } else { - deselectAllItems(TRUE); + deselectAllItems(true); selectItem(hit_item, getColumnIndexFromOffset(x)); } } @@ -1927,7 +1927,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) else { //mLastSelected = NULL; - //deselectAllItems(TRUE); + //deselectAllItems(true); } return selection_changed; @@ -1949,7 +1949,7 @@ bool LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) handleClick(x, y, mask); } - return TRUE; + return true; } bool LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) @@ -2030,7 +2030,7 @@ bool LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) menu->show(x, y); LLMenuGL::showPopup(this, menu, x, y); - return TRUE; + return true; } } return LLUICtrl::handleRightMouseDown(x, y, mask); @@ -2110,7 +2110,7 @@ void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group) bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) { - //BOOL handled = FALSE; + //bool handled = false; bool handled = handleClick(x, y, mask); if (!handled) @@ -2130,16 +2130,16 @@ bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) return true; } -BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) +bool LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) { // which row was clicked on? LLScrollListItem* hit_item = hitItem(x, y); - if (!hit_item) return FALSE; + if (!hit_item) return false; // get appropriate cell from that row S32 column_index = getColumnIndexFromOffset(x); LLScrollListCell* hit_cell = hit_item->getColumn(column_index); - if (!hit_cell) return FALSE; + if (!hit_cell) return false; // if cell handled click directly (i.e. clicked on an embedded checkbox) if (hit_cell->handleClick()) @@ -2175,7 +2175,7 @@ BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) onCommit(); } // eat click (e.g. do not trigger double click callback) - return TRUE; + return true; } else { @@ -2184,7 +2184,7 @@ BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) gFocusMgr.setMouseCapture(this); mNeedsScroll = true; // do not eat click (allow double click callback) - return FALSE; + return false; } } @@ -2337,9 +2337,9 @@ void LLScrollListCtrl::onMouseLeave(S32 x, S32 y, MASK mask) mouseOverHighlightNthItem(-1); } -BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) +bool LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { - BOOL handled = FALSE; + bool handled = false; // not called from parent means we have keyboard focus or a child does if (mCanSelect) @@ -2352,18 +2352,18 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) if (mAllowKeyboardMovement || hasFocus()) { // commit implicit in call - selectPrevItem(FALSE); + selectPrevItem(false); mNeedsScroll = true; - handled = TRUE; + handled = true; } break; case KEY_DOWN: if (mAllowKeyboardMovement || hasFocus()) { // commit implicit in call - selectNextItem(FALSE); + selectNextItem(false); mNeedsScroll = true; - handled = TRUE; + handled = true; } break; case KEY_LEFT: @@ -2388,7 +2388,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) break; } item->setSelectedCell(cell); - handled = TRUE; + handled = true; } } break; @@ -2414,7 +2414,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) break; } item->setSelectedCell(cell); - handled = TRUE; + handled = true; } } break; @@ -2428,7 +2428,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { onCommit(); } - handled = TRUE; + handled = true; } break; case KEY_PAGE_DOWN: @@ -2441,7 +2441,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { onCommit(); } - handled = TRUE; + handled = true; } break; case KEY_HOME: @@ -2454,7 +2454,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { onCommit(); } - handled = TRUE; + handled = true; } break; case KEY_END: @@ -2467,7 +2467,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { onCommit(); } - handled = TRUE; + handled = true; } break; case KEY_RETURN: @@ -2478,7 +2478,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { onCommit(); mSearchString.clear(); - handled = TRUE; + handled = true; } break; case KEY_BACKSPACE: @@ -2498,7 +2498,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) } } } - else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), FALSE)) + else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), false)) { mNeedsScroll = true; // update search string only on successful match @@ -2618,11 +2618,11 @@ void LLScrollListCtrl::reportInvalidInput() make_ui_sound("UISndBadKeystroke"); } -BOOL LLScrollListCtrl::isRepeatedChars(const LLWString& string) const +bool LLScrollListCtrl::isRepeatedChars(const LLWString& string) const { if (string.empty()) { - return FALSE; + return false; } llwchar first_char = string[0]; @@ -2631,14 +2631,14 @@ BOOL LLScrollListCtrl::isRepeatedChars(const LLWString& string) const { if (string[i] != first_char) { - return FALSE; + return false; } } - return TRUE; + return true; } -void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, BOOL select_single_item) +void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, bool select_single_item) { if (!itemp) return; @@ -2654,9 +2654,9 @@ void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, BOOL select } if (select_single_item) { - deselectAllItems(TRUE); + deselectAllItems(true); } - itemp->setSelected(TRUE); + itemp->setSelected(true); switch (mSelectionType) { case CELL: @@ -2685,7 +2685,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) mLastSelected = NULL; } - itemp->setSelected(FALSE); + itemp->setSelected(false); LLScrollListCell* cellp = itemp->getColumn(getSearchColumn()); if (cellp) { @@ -2700,7 +2700,7 @@ void LLScrollListCtrl::commitIfChanged() if (mSelectionChanged) { mDirty = true; - mSelectionChanged = FALSE; + mSelectionChanged = false; onCommit(); } } @@ -2710,13 +2710,13 @@ struct SameSortColumn SameSortColumn(S32 column) : mColumn(column) {} S32 mColumn; - bool operator()(std::pair sort_column) { return sort_column.first == mColumn; } + bool operator()(std::pair sort_column) { return sort_column.first == mColumn; } }; -BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending) +bool LLScrollListCtrl::setSort(S32 column_idx, bool ascending) { LLScrollListColumn* sort_column = getColumn(column_idx); - if (!sort_column) return FALSE; + if (!sort_column) return false; sort_column->mSortDirection = ascending ? LLScrollListColumn::ASCENDING : LLScrollListColumn::DESCENDING; @@ -2727,7 +2727,7 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending) if (mSortColumns.empty()) { mSortColumns.push_back(new_sort_column); - return TRUE; + return true; } else { @@ -2765,7 +2765,7 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar ) } -void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending) +void LLScrollListCtrl::sortByColumn(const std::string& name, bool ascending) { column_map_t::iterator itor = mColumns.find(name); if (itor != mColumns.end()) @@ -2775,7 +2775,7 @@ void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending) } // First column is column 0 -void LLScrollListCtrl::sortByColumnIndex(U32 column, BOOL ascending) +void LLScrollListCtrl::sortByColumnIndex(U32 column, bool ascending) { setSort(column, ascending); updateSort(); @@ -2796,9 +2796,9 @@ void LLScrollListCtrl::updateSort() const } // for one-shot sorts, does not save sort column/order -void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending) +void LLScrollListCtrl::sortOnce(S32 column, bool ascending) { - std::vector > sort_column; + std::vector > sort_column; sort_column.push_back(std::make_pair(column, ascending)); // do stable sort to preserve any previous sorts @@ -2901,7 +2901,7 @@ void LLScrollListCtrl::copy() } // virtual -BOOL LLScrollListCtrl::canCopy() const +bool LLScrollListCtrl::canCopy() const { return (getFirstSelected() != NULL); } @@ -2914,7 +2914,7 @@ void LLScrollListCtrl::cut() } // virtual -BOOL LLScrollListCtrl::canCut() const +bool LLScrollListCtrl::canCut() const { return canCopy() && canDoDelete(); } @@ -2929,7 +2929,7 @@ void LLScrollListCtrl::selectAll() LLScrollListItem *itemp = *iter; if( itemp->getEnabled() ) { - selectItem(itemp, -1, FALSE); + selectItem(itemp, -1, false); } } @@ -2940,7 +2940,7 @@ void LLScrollListCtrl::selectAll() } // virtual -BOOL LLScrollListCtrl::canSelectAll() const +bool LLScrollListCtrl::canSelectAll() const { return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable); } @@ -2952,7 +2952,7 @@ void LLScrollListCtrl::deselect() } // virtual -BOOL LLScrollListCtrl::canDeselect() const +bool LLScrollListCtrl::canDeselect() const { return getCanSelect(); } @@ -3093,7 +3093,7 @@ std::string LLScrollListCtrl::getSortColumnName() else return ""; } -BOOL LLScrollListCtrl::hasSortOrder() const +bool LLScrollListCtrl::hasSortOrder() const { return !mSortColumns.empty(); } @@ -3230,7 +3230,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS && cell->isText() && !cell->getValue().asString().empty()) { - columnp->mHeader->setHasResizableElement(TRUE); + columnp->mHeader->setHasResizableElement(true); } } @@ -3258,7 +3258,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS && cell->isText() && !cell->getValue().asString().empty()) { - columnp->mHeader->setHasResizableElement(TRUE); + columnp->mHeader->setHasResizableElement(true); } } } @@ -3315,26 +3315,26 @@ LLSD LLScrollListCtrl::getValue() const return item->getValue(); } -BOOL LLScrollListCtrl::operateOnSelection(EOperation op) +bool LLScrollListCtrl::operateOnSelection(EOperation op) { if (op == OP_DELETE) { deleteSelectedItems(); - return TRUE; + return true; } else if (op == OP_DESELECT) { deselectAllItems(); } - return FALSE; + return false; } -BOOL LLScrollListCtrl::operateOnAll(EOperation op) +bool LLScrollListCtrl::operateOnAll(EOperation op) { if (op == OP_DELETE) { clearRows(); - return TRUE; + return true; } else if (op == OP_DESELECT) { @@ -3344,10 +3344,10 @@ BOOL LLScrollListCtrl::operateOnAll(EOperation op) { selectAll(); } - return FALSE; + return false; } //virtual -void LLScrollListCtrl::setFocus(BOOL b) +void LLScrollListCtrl::setFocus(bool b) { // for tabbing into pristine scroll lists (Finder) if (!getFirstSelected()) @@ -3360,9 +3360,9 @@ void LLScrollListCtrl::setFocus(BOOL b) // virtual -BOOL LLScrollListCtrl::isDirty() const +bool LLScrollListCtrl::isDirty() const { - BOOL grubby = mDirty; + bool grubby = mDirty; if ( !mAllowMultipleSelection ) { grubby = (mOriginalSelection != getFirstSelectedIndex()); @@ -3373,7 +3373,7 @@ BOOL LLScrollListCtrl::isDirty() const // Clear dirty state void LLScrollListCtrl::resetDirty() { - mDirty = FALSE; + mDirty = false; mOriginalSelection = getFirstSelectedIndex(); } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index a121913579..1eb3e530e8 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -166,7 +166,7 @@ public: // Sets an array of column descriptors void setColumnHeadings(const LLSD& headings); - void sortByColumnIndex(U32 column, BOOL ascending); + void sortByColumnIndex(U32 column, bool ascending); // LLCtrlListInterface functions virtual S32 getItemCount() const; @@ -189,7 +189,7 @@ public: // Simple add element. Takes a single array of: // [ "value" => value, "font" => font, "font-style" => style ] virtual void clearRows(); // clears all elements - virtual void sortByColumn(const std::string& name, BOOL ascending); + virtual void sortByColumn(const std::string& name, bool ascending); // These functions take and return an array of arrays of elements, as above virtual void setValue(const LLSD& value ); @@ -200,37 +200,37 @@ public: LLCtrlScrollInterface* getScrollInterface() { return (LLCtrlScrollInterface*)this; } // DEPRECATED: Use setSelectedByValue() below. - BOOL setCurrentByID( const LLUUID& id ) { return selectByID(id); } + bool setCurrentByID( const LLUUID& id ) { return selectByID(id); } virtual LLUUID getCurrentID() const { return getStringUUIDSelectedItem(); } - BOOL operateOnSelection(EOperation op); - BOOL operateOnAll(EOperation op); + bool operateOnSelection(EOperation op); + bool operateOnAll(EOperation op); - // returns FALSE if unable to set the max count so low - BOOL setMaxItemCount(S32 max_count); + // returns false if unable to set the max count so low + bool setMaxItemCount(S32 max_count); - BOOL selectByID( const LLUUID& id ); // FALSE if item not found + bool selectByID( const LLUUID& id ); // false if item not found // Match item by value.asString(), which should work for string, integer, uuid. - // Returns FALSE if not found. - BOOL setSelectedByValue(const LLSD& value, BOOL selected); + // Returns false if not found. + bool setSelectedByValue(const LLSD& value, bool selected); - BOOL isSorted() const { return mSorted; } + bool isSorted() const { return mSorted; } - virtual BOOL isSelected(const LLSD& value) const; + virtual bool isSelected(const LLSD& value) const; - BOOL hasSelectedItem() const; + bool hasSelectedItem() const; - BOOL handleClick(S32 x, S32 y, MASK mask); - BOOL selectFirstItem(); - BOOL selectNthItem( S32 index ); - BOOL selectItemRange( S32 first, S32 last ); - BOOL selectItemAt(S32 x, S32 y, MASK mask); + bool handleClick(S32 x, S32 y, MASK mask); + bool selectFirstItem(); + bool selectNthItem( S32 index ); + bool selectItemRange( S32 first, S32 last ); + bool selectItemAt(S32 x, S32 y, MASK mask); void deleteSingleItem( S32 index ); void deleteItems(const LLSD& sd); void deleteSelectedItems(); - void deselectAllItems(BOOL no_commit_on_change = FALSE); // by default, go ahead and commit on selection change + void deselectAllItems(bool no_commit_on_change = false); // by default, go ahead and commit on selection change void clearHighlightedItems(); @@ -247,8 +247,8 @@ public: void swapWithNext(S32 index); void swapWithPrevious(S32 index); - void setCanSelect(BOOL can_select) { mCanSelect = can_select; } - virtual BOOL getCanSelect() const { return mCanSelect; } + void setCanSelect(bool can_select) { mCanSelect = can_select; } + virtual bool getCanSelect() const { return mCanSelect; } S32 getItemIndex( LLScrollListItem* item ) const; S32 getItemIndex( const LLUUID& item_id ) const; @@ -260,10 +260,10 @@ public: // one of which can be selected at a time. virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD()); - BOOL selectItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 ); // FALSE if item not found - BOOL selectItemByPrefix(const std::string& target, BOOL case_sensitive = TRUE, S32 column = -1); - BOOL selectItemByPrefix(const LLWString& target, BOOL case_sensitive = TRUE, S32 column = -1); - LLScrollListItem* getItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 ); + bool selectItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 ); // false if item not found + bool selectItemByPrefix(const std::string& target, bool case_sensitive = true, S32 column = -1); + bool selectItemByPrefix(const LLWString& target, bool case_sensitive = true, S32 column = -1); + LLScrollListItem* getItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 ); const std::string getSelectedItemLabel(S32 column = 0) const; LLSD getSelectedValue(); @@ -278,7 +278,7 @@ public: // DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue(). // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which // has an associated, unique UUID, and only one of which can be selected at a time. - LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true); LLUUID getStringUUIDSelectedItem() const; LLScrollListItem* getFirstSelected() const; @@ -294,7 +294,7 @@ public: LLScrollListItem* getItem(const LLSD& sd) const; - void setAllowMultipleSelection(BOOL mult ) { mAllowMultipleSelection = mult; } + void setAllowMultipleSelection(bool mult ) { mAllowMultipleSelection = mult; } void setBgWriteableColor(const LLColor4 &c) { mBgWriteableColor = c; } void setReadOnlyBgColor(const LLColor4 &c) { mBgReadOnlyColor = c; } @@ -306,15 +306,15 @@ public: void setHighlightedColor(const LLColor4 &c) { mHighlightedColor = c; } void setFgDisableColor(const LLColor4 &c) { mFgDisabledColor = c; } - void setBackgroundVisible(BOOL b) { mBackgroundVisible = b; } - void setDrawStripes(BOOL b) { mDrawStripes = b; } + void setBackgroundVisible(bool b) { mBackgroundVisible = b; } + void setDrawStripes(bool b) { mDrawStripes = b; } void setColumnPadding(const S32 c) { mColumnPadding = c; } S32 getColumnPadding() const { return mColumnPadding; } void setRowPadding(const S32 c) { mColumnPadding = c; } S32 getRowPadding() const { return mColumnPadding; } - void setCommitOnKeyboardMovement(BOOL b) { mCommitOnKeyboardMovement = b; } - void setCommitOnSelectionChange(BOOL b) { mCommitOnSelectionChange = b; } - void setAllowKeyboardMovement(BOOL b) { mAllowKeyboardMovement = b; } + void setCommitOnKeyboardMovement(bool b) { mCommitOnKeyboardMovement = b; } + void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; } + void setAllowKeyboardMovement(bool b) { mAllowKeyboardMovement = b; } void setMaxSelectable(U32 max_selected) { mMaxSelectable = max_selected; } S32 getMaxSelectable() { return mMaxSelectable; } @@ -342,26 +342,26 @@ public: /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask); /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - /*virtual*/ void setEnabled(BOOL enabled); - /*virtual*/ void setFocus( BOOL b ); + /*virtual*/ void setEnabled(bool enabled); + /*virtual*/ void setFocus( bool b ); /*virtual*/ void onFocusReceived(); /*virtual*/ void onFocusLost(); /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - virtual BOOL isDirty() const; + virtual bool isDirty() const; virtual void resetDirty(); // Clear dirty state virtual void updateLayout(); virtual void fitContents(S32 max_width, S32 max_height); virtual LLRect getRequiredRect(); - static BOOL rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row); + static bool rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row); LLRect getItemListRect() { return mItemListRect; } @@ -383,28 +383,28 @@ public: * then display all items. */ void setPageLines(S32 page_lines ); - void setCollapseEmptyColumns(BOOL collapse); + void setCollapseEmptyColumns(bool collapse); LLScrollListItem* hitItem(S32 x,S32 y); virtual void scrollToShowSelected(); // LLEditMenuHandler functions virtual void copy(); - virtual BOOL canCopy() const; + virtual bool canCopy() const; virtual void cut(); - virtual BOOL canCut() const; + virtual bool canCut() const; virtual void selectAll(); - virtual BOOL canSelectAll() const; + virtual bool canSelectAll() const; virtual void deselect(); - virtual BOOL canDeselect() const; + virtual bool canDeselect() const; void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; } void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width); S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; } std::string getSortColumnName(); - BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } - BOOL hasSortOrder() const; + bool getSortAscending() { return mSortColumns.empty() ? true : mSortColumns.back().second; } + bool hasSortOrder() const; void clearSortOrder(); void setAlternateSort() { mAlternateSort = true; } @@ -413,7 +413,7 @@ public: // conceptually const, but mutates mItemList void updateSort() const; // sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example) - void sortOnce(S32 column, BOOL ascending); + void sortOnce(S32 column, bool ascending); // manually call this whenever editing list items in place to flag need for resorting void setNeedsSort(bool val = true) { mSorted = !val; } @@ -445,8 +445,8 @@ protected: // (except in the case that the addItem() call fails, in which case it is up // to the caller to delete the item) // - // returns FALSE if item faile to be added to list, does NOT delete 'item' - BOOL addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM, BOOL requires_column = TRUE ); + // returns false if item faile to be added to list, does NOT delete 'item' + bool addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM, bool requires_column = true ); typedef std::deque item_list; item_list& getItemList() { return mItemList; } @@ -454,17 +454,17 @@ protected: void updateLineHeight(); private: - void selectPrevItem(BOOL extend_selection); - void selectNextItem(BOOL extend_selection); + void selectPrevItem(bool extend_selection); + void selectNextItem(bool extend_selection); void drawItems(); void updateLineHeightInsert(LLScrollListItem* item); void reportInvalidInput(); - BOOL isRepeatedChars(const LLWString& string) const; - void selectItem(LLScrollListItem* itemp, S32 cell, BOOL single_select = TRUE); + bool isRepeatedChars(const LLWString& string) const; + void selectItem(LLScrollListItem* itemp, S32 cell, bool single_select = true); void deselectItem(LLScrollListItem* itemp); void commitIfChanged(); - BOOL setSort(S32 column, BOOL ascending); + bool setSort(S32 column, bool ascending); S32 getLinesPerPage(); static void showProfile(std::string id, bool is_group); @@ -508,8 +508,8 @@ private: S32 mColumnPadding; S32 mRowPadding; - BOOL mBackgroundVisible; - BOOL mDrawStripes; + bool mBackgroundVisible; + bool mDrawStripes; LLUIColor mBgWriteableColor; LLUIColor mBgReadOnlyColor; @@ -553,7 +553,7 @@ private: typedef std::vector ordered_columns_t; ordered_columns_t mColumnsIndexed; - typedef std::pair sort_column_t; + typedef std::pair sort_column_t; std::vector mSortColumns; sort_signal_t* mSortCallback; diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp index e1360f80cd..6d360a1bd9 100644 --- a/indra/llui/llscrolllistitem.cpp +++ b/indra/llui/llscrolllistitem.cpp @@ -38,8 +38,8 @@ //--------------------------------------------------------------------------- LLScrollListItem::LLScrollListItem( const Params& p ) -: mSelected(FALSE), - mHighlighted(FALSE), +: mSelected(false), + mHighlighted(false), mHoverIndex(-1), mSelectedIndex(-1), mEnabled(p.enabled), @@ -56,13 +56,13 @@ LLScrollListItem::~LLScrollListItem() mColumns.clear(); } -void LLScrollListItem::setSelected(BOOL b) +void LLScrollListItem::setSelected(bool b) { mSelected = b; mSelectedIndex = -1; } -void LLScrollListItem::setHighlighted(BOOL b) +void LLScrollListItem::setHighlighted(bool b) { mHighlighted = b; mHoverIndex = -1; diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h index a3398305b1..a6fa05cd44 100644 --- a/indra/llui/llscrolllistitem.h +++ b/indra/llui/llscrolllistitem.h @@ -79,14 +79,14 @@ public: virtual ~LLScrollListItem(); - void setSelected( BOOL b ); - BOOL getSelected() const { return mSelected; } + void setSelected( bool b ); + bool getSelected() const { return mSelected; } - void setEnabled( BOOL b ) { mEnabled = b; } - BOOL getEnabled() const { return mEnabled; } + void setEnabled( bool b ) { mEnabled = b; } + bool getEnabled() const { return mEnabled; } - void setHighlighted( BOOL b ); - BOOL getHighlighted() const { return mHighlighted; } + void setHighlighted( bool b ); + bool getHighlighted() const { return mHighlighted; } void setSelectedCell( S32 cell ); S32 getSelectedCell() const { return mSelectedIndex; } @@ -127,11 +127,11 @@ protected: LLScrollListItem( const Params& ); private: - BOOL mSelected; - BOOL mHighlighted; + bool mSelected; + bool mHighlighted; S32 mHoverIndex; S32 mSelectedIndex; - BOOL mEnabled; + bool mEnabled; void* mUserdata; LLSD mItemValue; LLSD mItemAltValue; diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index bafeef41fb..78bd06b67e 100644 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -72,7 +72,7 @@ LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p) line_editor_params.keystroke_callback(boost::bind(&LLSearchEditor::handleKeystroke, this)); mSearchEditor = LLUICtrlFactory::create(line_editor_params); - mSearchEditor->setPassDelete(TRUE); + mSearchEditor->setPassDelete(true); addChild(mSearchEditor); if (p.search_button_visible) @@ -140,13 +140,13 @@ LLSD LLSearchEditor::getValue() const } //virtual -BOOL LLSearchEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) +bool LLSearchEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) { return mSearchEditor->setTextArg(key, text); } //virtual -BOOL LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) { return mSearchEditor->setLabelArg(key, text); } @@ -167,7 +167,7 @@ void LLSearchEditor::clear() } //virtual -void LLSearchEditor::setFocus( BOOL b ) +void LLSearchEditor::setFocus( bool b ) { if (mSearchEditor) { diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h index c0f3c1d60c..b332967f9b 100644 --- a/indra/llui/llsearcheditor.h +++ b/indra/llui/llsearcheditor.h @@ -67,7 +67,7 @@ public: {} }; - void setCommitOnFocusLost(BOOL b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); } + void setCommitOnFocusLost(bool b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); } protected: LLSearchEditor(const Params&); @@ -84,11 +84,11 @@ public: // LLUICtrl interface virtual void setValue(const LLSD& value ); virtual LLSD getValue() const; - virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text ); - virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setTextArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); virtual void setLabel( const LLStringExplicit &new_label ); virtual void clear(); - virtual void setFocus( BOOL b ); + virtual void setFocus( bool b ); void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; } void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index e759e7716e..c1c761c8fa 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -93,7 +93,7 @@ LLSlider::~LLSlider() delete mMouseUpSignal; } -void LLSlider::setValue(F32 value, BOOL from_event) +void LLSlider::setValue(F32 value, bool from_event) { value = llclamp( value, mMinValue, mMaxValue ); @@ -256,20 +256,20 @@ bool LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) return true; } -BOOL LLSlider::handleKeyHere(KEY key, MASK mask) +bool LLSlider::handleKeyHere(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; switch(key) { case KEY_DOWN: case KEY_LEFT: setValueAndCommit(getValueF32() - getIncrement()); - handled = TRUE; + handled = true; break; case KEY_UP: case KEY_RIGHT: setValueAndCommit(getValueF32() + getIncrement()); - handled = TRUE; + handled = true; break; default: break; diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index ad3df1da82..767fcd3bbd 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -60,9 +60,9 @@ protected: friend class LLUICtrlFactory; public: virtual ~LLSlider(); - void setValue( F32 value, BOOL from_event = FALSE ); + void setValue( F32 value, bool from_event = false ); // overrides for LLF32UICtrl methods - virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), TRUE); } + virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), true); } virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } @@ -75,7 +75,7 @@ public: virtual bool handleHover(S32 x, S32 y, MASK mask); virtual bool handleMouseUp(S32 x, S32 y, MASK mask); virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); virtual void draw(); @@ -83,7 +83,7 @@ private: void setValueAndCommit(F32 value); void updateThumbRect(); - BOOL mVolumeSlider; + bool mVolumeSlider; S32 mMouseOffset; LLRect mDragStartThumbRect; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index d80a434f22..2bfb3a624b 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -173,7 +173,7 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p) mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this )); // don't do this, as selecting the entire text is single clicking in some cases // and double clicking in others - //mEditor->setSelectAllonFocusReceived(TRUE); + //mEditor->setSelectAllonFocusReceived(true); addChild(mEditor); } else @@ -210,16 +210,16 @@ void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata } -void LLSliderCtrl::setValue(F32 v, BOOL from_event) +void LLSliderCtrl::setValue(F32 v, bool from_event) { mSlider->setValue( v, from_event ); mValue = mSlider->getValueF32(); updateText(); } -BOOL LLSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) { - BOOL res = FALSE; + bool res = false; if (mLabelBox) { res = mLabelBox->setTextArg(key, text); @@ -319,7 +319,7 @@ void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata ) if (!self) return; - BOOL success = FALSE; + bool success = false; F32 val = self->mValue; F32 saved_val = self->mValue; @@ -333,7 +333,7 @@ void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata ) self->setValue( val ); // set the value temporarily so that the callback can retrieve it. if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) ) { - success = TRUE; + success = true; } } } @@ -362,14 +362,14 @@ void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata ) if (!self) return; - BOOL success = FALSE; + bool success = false; F32 saved_val = self->mValue; F32 new_val = self->mSlider->getValueF32(); self->mValue = new_val; // set the value temporarily so that the callback can retrieve it. if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) ) { - success = TRUE; + success = true; } if( success ) @@ -387,7 +387,7 @@ void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata ) self->updateText(); } -void LLSliderCtrl::setEnabled(BOOL b) +void LLSliderCtrl::setEnabled(bool b) { LLView::setEnabled( b ); @@ -410,7 +410,7 @@ void LLSliderCtrl::setEnabled(BOOL b) } -void LLSliderCtrl::setTentative(BOOL b) +void LLSliderCtrl::setTentative(bool b) { if( mEditor ) { @@ -422,11 +422,11 @@ void LLSliderCtrl::setTentative(BOOL b) void LLSliderCtrl::onCommit() { - setTentative(FALSE); + setTentative(false); if( mEditor ) { - mEditor->setTentative(FALSE); + mEditor->setTentative(false); } setControlValue(getValueF32()); @@ -440,7 +440,7 @@ void LLSliderCtrl::setRect(const LLRect& rect) } //virtual -void LLSliderCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLSliderCtrl::reshape(S32 width, S32 height, bool called_from_parent) { LLF32UICtrl::reshape(width, height, called_from_parent); updateSliderRect(); diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h index 541c167717..810162df16 100644 --- a/indra/llui/llsliderctrl.h +++ b/indra/llui/llsliderctrl.h @@ -84,17 +84,17 @@ public: virtual ~LLSliderCtrl(); /*virtual*/ F32 getValueF32() const { return mSlider->getValueF32(); } - void setValue(F32 v, BOOL from_event = FALSE); + void setValue(F32 v, bool from_event = false); - /*virtual*/ void setValue(const LLSD& value) { setValue((F32)value.asReal(), TRUE); } + /*virtual*/ void setValue(const LLSD& value) { setValue((F32)value.asReal(), true); } /*virtual*/ LLSD getValue() const { return LLSD(getValueF32()); } - /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); + /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - BOOL isMouseHeldDown() const { return mSlider->hasMouseCapture(); } + bool isMouseHeldDown() const { return mSlider->hasMouseCapture(); } virtual void setPrecision(S32 precision); - /*virtual*/ void setEnabled( BOOL b ); + /*virtual*/ void setEnabled( bool b ); /*virtual*/ void clear(); /*virtual*/ void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } @@ -116,7 +116,7 @@ public: /*virtual*/ void onTabInto(); - /*virtual*/ void setTentative(BOOL b); // marks value as tentative + /*virtual*/ void setTentative(bool b); // marks value as tentative /*virtual*/ void onCommit(); // mark not tentative, then commit /*virtual*/ void setControlName(const std::string& control_name, LLView* context) @@ -126,7 +126,7 @@ public: } /*virtual*/ void setRect(const LLRect& rect); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata); @@ -154,8 +154,8 @@ private: const LLFontGL* mFont; const LLFontGL* mLabelFont; - BOOL mShowText; - BOOL mCanEditText; + bool mShowText; + bool mCanEditText; S32 mPrecision; LLTextBox* mLabelBox; diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 3a3ecb2d85..84ac3a8c0f 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -63,7 +63,7 @@ LLSpinCtrl::Params::Params() LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) : LLF32UICtrl(p), mLabelBox(NULL), - mbHasBeenSet( FALSE ), + mbHasBeenSet( false ), mPrecision(p.decimal_digits), mTextEnabledColor(p.text_enabled_color()), mTextDisabledColor(p.text_disabled_color()) @@ -143,12 +143,12 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) //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 - //mEditor->setSelectAllonFocusReceived(TRUE); - mEditor->setSelectAllonCommit(FALSE); + //mEditor->setSelectAllonFocusReceived(true); + mEditor->setSelectAllonCommit(false); addChild(mEditor); updateEditor(); - setUseBoundingRect( TRUE ); + setUseBoundingRect( true ); } F32 clamp_precision(F32 value, S32 decimal_precision) @@ -275,7 +275,7 @@ void LLSpinCtrl::setValue(const LLSD& value ) F32 v = (F32)value.asReal(); if (getValueF32() != v || !mbHasBeenSet) { - mbHasBeenSet = TRUE; + mbHasBeenSet = true; LLF32UICtrl::setValue(value); if (!mEditor->hasFocus()) @@ -291,7 +291,7 @@ void LLSpinCtrl::forceSetValue(const LLSD& value ) F32 v = (F32)value.asReal(); if (getValueF32() != v || !mbHasBeenSet) { - mbHasBeenSet = TRUE; + mbHasBeenSet = true; LLF32UICtrl::setValue(value); updateEditor(); @@ -303,7 +303,7 @@ void LLSpinCtrl::clear() { setValue(mMinValue); mEditor->clear(); - mbHasBeenSet = FALSE; + mbHasBeenSet = false; } void LLSpinCtrl::updateLabelColor() @@ -333,7 +333,7 @@ void LLSpinCtrl::updateEditor() void LLSpinCtrl::onEditorCommit( const LLSD& data ) { - BOOL success = FALSE; + bool success = false; if( mEditor->evaluateFloat() ) { @@ -349,7 +349,7 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data ) setValue(val); if( !mValidateSignal || (*mValidateSignal)( this, val ) ) { - success = TRUE; + success = true; onCommit(); } else @@ -378,13 +378,13 @@ void LLSpinCtrl::forceEditorCommit() } -void LLSpinCtrl::setFocus(BOOL b) +void LLSpinCtrl::setFocus(bool b) { LLUICtrl::setFocus( b ); mEditor->setFocus( b ); } -void LLSpinCtrl::setEnabled(BOOL b) +void LLSpinCtrl::setEnabled(bool b) { LLView::setEnabled( b ); mEditor->setEnabled( b ); @@ -392,14 +392,14 @@ void LLSpinCtrl::setEnabled(BOOL b) } -void LLSpinCtrl::setTentative(BOOL b) +void LLSpinCtrl::setTentative(bool b) { mEditor->setTentative(b); LLUICtrl::setTentative(b); } -BOOL LLSpinCtrl::isMouseHeldDown() const +bool LLSpinCtrl::isMouseHeldDown() const { return mDownBtn->hasMouseCapture() @@ -408,7 +408,7 @@ BOOL LLSpinCtrl::isMouseHeldDown() const void LLSpinCtrl::onCommit() { - setTentative(FALSE); + setTentative(false); setControlValue(getValueF32()); LLF32UICtrl::onCommit(); } @@ -439,7 +439,7 @@ void LLSpinCtrl::setLabel(const LLStringExplicit& label) updateLabelColor(); } -void LLSpinCtrl::setAllowEdit(BOOL allow_edit) +void LLSpinCtrl::setAllowEdit(bool allow_edit) { mEditor->setEnabled(allow_edit); mAllowEdit = allow_edit; @@ -475,7 +475,7 @@ bool LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) return true; } -BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask) +bool LLSpinCtrl::handleKeyHere(KEY key, MASK mask) { if (mEditor->hasFocus()) { @@ -485,20 +485,20 @@ BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask) // but not allowing revert on a spinner seems dangerous updateEditor(); mEditor->resetScrollPosition(); - mEditor->setFocus(FALSE); - return TRUE; + mEditor->setFocus(false); + return true; } if(key == KEY_UP) { onUpBtn(getValue()); - return TRUE; + return true; } if(key == KEY_DOWN) { onDownBtn(getValue()); - return TRUE; + return true; } } - return FALSE; + return false; } diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index 8d22693021..046d15eaf7 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -66,12 +66,12 @@ public: F32 get() const { return getValueF32(); } void set(F32 value) { setValue(value); mInitialValue = value; } - BOOL isMouseHeldDown() const; + bool isMouseHeldDown() const; - virtual void setEnabled( BOOL b ); - virtual void setFocus( BOOL b ); + virtual void setEnabled( bool b ); + virtual void setFocus( bool b ); virtual void clear(); - virtual BOOL isDirty() const { return( getValueF32() != mInitialValue ); } + virtual bool isDirty() const { return( getValueF32() != mInitialValue ); } virtual void resetDirty() { mInitialValue = getValueF32(); } virtual void setPrecision(S32 precision); @@ -79,17 +79,17 @@ public: void setLabel(const LLStringExplicit& label); void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; updateLabelColor(); } void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; updateLabelColor();} - void setAllowEdit(BOOL allow_edit); + void setAllowEdit(bool allow_edit); virtual void onTabInto(); - virtual void setTentative(BOOL b); // marks value as tentative + virtual void setTentative(bool b); // marks value as tentative virtual void onCommit(); // mark not tentative, then commit void forceEditorCommit(); // for commit on external button virtual bool handleScrollWheel(S32 x,S32 y,S32 clicks); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); void onEditorCommit(const LLSD& data); static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); @@ -117,8 +117,8 @@ private: class LLButton* mUpBtn; class LLButton* mDownBtn; - BOOL mbHasBeenSet; - BOOL mAllowEdit; + bool mbHasBeenSet; + bool mAllowEdit; }; #endif // LL_LLSPINCTRL_H diff --git a/indra/llui/llstatgraph.cpp b/indra/llui/llstatgraph.cpp index 3fe314e77a..ec014a7419 100644 --- a/indra/llui/llstatgraph.cpp +++ b/indra/llui/llstatgraph.cpp @@ -104,14 +104,14 @@ void LLStatGraph::draw() color = LLUIColorTable::instance().getColor( "MenuDefaultBgColor" ); gGL.color4fv(color.mV); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, TRUE); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, true); gGL.color4fv(LLColor4::black.mV); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, FALSE); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, false); color = it->mColor; gGL.color4fv(color.mV); - gl_rect_2d(1, ll_round(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, TRUE); + gl_rect_2d(1, ll_round(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, true); } void LLStatGraph::setMin(const F32 min) diff --git a/indra/llui/llstatgraph.h b/indra/llui/llstatgraph.h index ba7cfc5d10..e70c745b3b 100644 --- a/indra/llui/llstatgraph.h +++ b/indra/llui/llstatgraph.h @@ -106,7 +106,7 @@ public: private: LLTrace::StatType* mNewStatFloatp; - BOOL mPerSec; + bool mPerSec; F32 mValue; diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index bb731f4f7e..5b8b9a4e35 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -73,17 +73,17 @@ void LLStyle::setLinkHREF(const std::string& href) mLink = href; } -BOOL LLStyle::isLink() const +bool LLStyle::isLink() const { return mIsLink; } -BOOL LLStyle::isVisible() const +bool LLStyle::isVisible() const { return mVisible; } -void LLStyle::setVisible(BOOL is_visible) +void LLStyle::setVisible(bool is_visible) { mVisible = is_visible; } diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index 9f1eba79d8..906df1762a 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -61,8 +61,8 @@ public: const LLUIColor& getSelectedColor() const { return mSelectedColor; } void setSelectedColor(const LLUIColor& color) { mSelectedColor = color; } - BOOL isVisible() const; - void setVisible(BOOL is_visible); + bool isVisible() const; + void setVisible(bool is_visible); LLFontGL::ShadowType getShadowType() const { return mDropShadow; } @@ -71,13 +71,13 @@ public: const std::string& getLinkHREF() const { return mLink; } void setLinkHREF(const std::string& href); - BOOL isLink() const; + bool isLink() const; LLPointer getImage() const; void setImage(const LLUUID& src); void setImage(const std::string& name); - BOOL isImage() const { return mImagep.notNull(); } + bool isImage() const { return mImagep.notNull(); } bool operator==(const LLStyle &rhs) const { @@ -101,7 +101,7 @@ protected: ~LLStyle() { } private: - BOOL mVisible; + bool mVisible; LLUIColor mColor; LLUIColor mReadOnlyColor; LLUIColor mSelectedColor; diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 512b2a9a30..1328510a07 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -75,7 +75,7 @@ public: mTabContainer(c), mTabPanel(p), mButton(b), - mOldState(FALSE), + mOldState(false), mPlaceholderText(placeholder), mPadding(0), mVisible(true) @@ -84,7 +84,7 @@ public: LLTabContainer* mTabContainer; LLPanel* mTabPanel; LLButton* mButton; - BOOL mOldState; + bool mOldState; LLTextBox* mPlaceholderText; S32 mPadding; @@ -232,7 +232,7 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p) : LLPanel(p), mCurrentTabIdx(-1), mTabsHidden(p.hide_tabs), - mScrolled(FALSE), + mScrolled(false), mScrollPos(0), mScrollPosPixels(0), mMaxScrollPos(0), @@ -306,14 +306,14 @@ void LLTabContainer::setValue(const LLSD& value) } //virtual -void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLTabContainer::reshape(S32 width, S32 height, bool called_from_parent) { LLPanel::reshape( width, height, called_from_parent ); updateMaxScrollPos(); } //virtual -LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse) const +LLView* LLTabContainer::getChildView(const std::string& name, bool recurse) const { tuple_list_t::const_iterator itor; for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) @@ -341,7 +341,7 @@ LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse) cons } //virtual -LLView* LLTabContainer::findChildView(const std::string& name, BOOL recurse) const +LLView* LLTabContainer::findChildView(const std::string& name, bool recurse) const { tuple_list_t::const_iterator itor; for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) @@ -383,11 +383,11 @@ bool LLTabContainer::addChild(LLView* view, S32 tab_group) } } -BOOL LLTabContainer::postBuild() +bool LLTabContainer::postBuild() { selectFirstTab(); - return TRUE; + return true; } // virtual @@ -431,7 +431,7 @@ void LLTabContainer::draw() setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLSmoothInterpolation::getInterpolant(0.08f))); - BOOL has_scroll_arrows = !mHideScrollArrows && !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0)); + bool has_scroll_arrows = !mHideScrollArrows && !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0)); if (!mIsVertical) { mJumpPrevArrowBtn->setVisible( has_scroll_arrows ); @@ -459,7 +459,7 @@ void LLTabContainer::draw() for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( FALSE ); + tuple->mButton->setVisible( false ); } } @@ -478,7 +478,7 @@ void LLTabContainer::draw() for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( TRUE ); + tuple->mButton->setVisible( true ); } S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos(); @@ -504,14 +504,14 @@ void LLTabContainer::draw() { if( tuple->mButton->getFlashing() ) { - mPrevArrowBtn->setFlashing( TRUE ); + mPrevArrowBtn->setFlashing( true ); } } else if( max_scroll_visible < idx ) { if( tuple->mButton->getFlashing() ) { - mNextArrowBtn->setFlashing( TRUE ); + mNextArrowBtn->setFlashing( true ); } } } @@ -604,7 +604,7 @@ bool LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) index = llclamp(index, 0, tab_count-1); LLButton* tab_button = getTab(index)->mButton; gFocusMgr.setMouseCapture(this); - tab_button->setFocus(TRUE); + tab_button->setFocus(true); mMouseDownTimer.start(); } } @@ -772,25 +772,25 @@ bool LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask) } // virtual -BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask) +bool LLTabContainer::handleKeyHere(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if (key == KEY_LEFT && mask == MASK_ALT) { selectPrevTab(); - handled = TRUE; + handled = true; } else if (key == KEY_RIGHT && mask == MASK_ALT) { selectNextTab(); - handled = TRUE; + handled = true; } if (handled) { if (getCurrentPanel()) { - getCurrentPanel()->setFocus(TRUE); + getCurrentPanel()->setFocus(true); } } @@ -803,21 +803,21 @@ BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask) { case KEY_UP: selectPrevTab(); - handled = TRUE; + handled = true; break; case KEY_DOWN: selectNextTab(); - handled = TRUE; + handled = true; break; case KEY_LEFT: - handled = TRUE; + handled = true; break; case KEY_RIGHT: if (getTabPosition() == LEFT && getCurrentPanel()) { - getCurrentPanel()->setFocus(TRUE); + getCurrentPanel()->setFocus(true); } - handled = TRUE; + handled = true; break; default: break; @@ -830,24 +830,24 @@ BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask) case KEY_UP: if (getTabPosition() == BOTTOM && getCurrentPanel()) { - getCurrentPanel()->setFocus(TRUE); + getCurrentPanel()->setFocus(true); } - handled = TRUE; + handled = true; break; case KEY_DOWN: if (getTabPosition() == TOP && getCurrentPanel()) { - getCurrentPanel()->setFocus(TRUE); + getCurrentPanel()->setFocus(true); } - handled = TRUE; + handled = true; break; case KEY_LEFT: selectPrevTab(); - handled = TRUE; + handled = true; break; case KEY_RIGHT: selectNextTab(); - handled = TRUE; + handled = true; break; default: break; @@ -858,9 +858,9 @@ BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask) } // virtual -BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string &tooltip) +bool LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string &tooltip) { - BOOL has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); if(mOpenTabsOnDragAndDrop && !getTabsHidden()) { @@ -901,7 +901,7 @@ BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDrag for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( TRUE ); + tuple->mButton->setVisible( true ); S32 local_x = x - tuple->mButton->getRect().mLeft; S32 local_y = y - tuple->mButton->getRect().mBottom; if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible()) @@ -964,9 +964,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) const std::string& label = panel.label.isProvided() ? panel.label() : panel.panel()->getLabel(); - BOOL select = panel.select_tab(); + bool select = panel.select_tab(); S32 indent = panel.indent(); - BOOL placeholder = panel.is_placeholder; + bool placeholder = panel.is_placeholder; eInsertionPoint insertion_point = panel.insert_at(); static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); @@ -1030,10 +1030,10 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) } child->setFollowsAll(); child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom); - child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE ); + child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), true ); // add this child later - child->setVisible( FALSE ); // Will be made visible when selected + child->setVisible( false ); // Will be made visible when selected mTotalTabWidth += button_width; @@ -1255,7 +1255,7 @@ void LLTabContainer::removeTabPanel(LLPanel* child) } } - BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this); + bool has_focus = gFocusMgr.childHasKeyboardFocus(this); // If the tab being deleted is the selected one, select a different tab. for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) @@ -1305,7 +1305,7 @@ void LLTabContainer::removeTabPanel(LLPanel* child) LLPanel* panelp = getPanelByIndex(mCurrentTabIdx); if (panelp) { - panelp->setFocus(TRUE); + panelp->setFocus(true); } } @@ -1324,7 +1324,7 @@ void LLTabContainer::unlockTabs() mLockedTabCount = 0; } -void LLTabContainer::enableTabButton(S32 which, BOOL enable) +void LLTabContainer::enableTabButton(S32 which, bool enable) { if (which >= 0 && which < (S32)mTabList.size()) { @@ -1453,10 +1453,10 @@ void LLTabContainer::selectNextTab() return; } - BOOL tab_has_focus = FALSE; + bool tab_has_focus = false; if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus()) { - tab_has_focus = TRUE; + tab_has_focus = true; } S32 idx = mCurrentTabIdx+1; if (idx >= (S32)mTabList.size()) @@ -1468,16 +1468,16 @@ void LLTabContainer::selectNextTab() if (tab_has_focus) { - mTabList[idx]->mButton->setFocus(TRUE); + mTabList[idx]->mButton->setFocus(true); } } void LLTabContainer::selectPrevTab() { - BOOL tab_has_focus = FALSE; + bool tab_has_focus = false; if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus()) { - tab_has_focus = TRUE; + tab_has_focus = true; } S32 idx = mCurrentTabIdx-1; if (idx < 0) @@ -1490,11 +1490,11 @@ void LLTabContainer::selectPrevTab() } if (tab_has_focus) { - mTabList[idx]->mButton->setFocus(TRUE); + mTabList[idx]->mButton->setFocus(true); } } -BOOL LLTabContainer::selectTabPanel(LLPanel* child) +bool LLTabContainer::selectTabPanel(LLPanel* child) { S32 idx = 0; for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) @@ -1506,25 +1506,25 @@ BOOL LLTabContainer::selectTabPanel(LLPanel* child) } idx++; } - return FALSE; + return false; } -BOOL LLTabContainer::selectTab(S32 which) +bool LLTabContainer::selectTab(S32 which) { if (which >= getTabCount() || which < 0) - return FALSE; + return false; LLTabTuple* selected_tuple = getTab(which); if (!selected_tuple) { - return FALSE; + return false; } LLSD cbdata; if (selected_tuple->mTabPanel) cbdata = selected_tuple->mTabPanel->getName(); - BOOL res = FALSE; + bool res = false; if( !mValidateSignal || (*mValidateSignal)( this, cbdata ) ) { res = setTab(which); @@ -1538,16 +1538,16 @@ BOOL LLTabContainer::selectTab(S32 which) } // private -BOOL LLTabContainer::setTab(S32 which) +bool LLTabContainer::setTab(S32 which) { static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); LLTabTuple* selected_tuple = getTab(which); if (!selected_tuple) { - return FALSE; + return false; } - BOOL is_visible = FALSE; + bool is_visible = false; if( selected_tuple->mButton->getEnabled() && selected_tuple->mVisible ) { setCurrentPanelIndex(which); @@ -1556,7 +1556,7 @@ BOOL LLTabContainer::setTab(S32 which) for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; - BOOL is_selected = ( tuple == selected_tuple ); + bool is_selected = ( tuple == selected_tuple ); // Although the selected tab must be complete, we may have hollow LLTabTuple tucked in the list if (tuple && tuple->mButton) { @@ -1583,11 +1583,11 @@ BOOL LLTabContainer::setTab(S32 which) if( i >= getScrollPos() && i <= getScrollPos() + num_visible) { setCurrentPanelIndex(which); - is_visible = TRUE; + is_visible = true; } else { - is_visible = FALSE; + is_visible = false; } } else if (!mHideScrollArrows && getMaxScrollPos() > 0) @@ -1619,11 +1619,11 @@ BOOL LLTabContainer::setTab(S32 which) setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i)); setScrollPos(llmin(getScrollPos(), getMaxScrollPos())); } - is_visible = TRUE; + is_visible = true; } else { - is_visible = TRUE; + is_visible = true; } } i++; @@ -1632,36 +1632,36 @@ BOOL LLTabContainer::setTab(S32 which) if (mIsVertical && getCurrentPanelIndex() >= 0) { LLTabTuple* tuple = getTab(getCurrentPanelIndex()); - tuple->mTabPanel->setVisible( TRUE ); - tuple->mButton->setToggleState( TRUE ); + tuple->mTabPanel->setVisible( true ); + tuple->mButton->setToggleState( true ); } return is_visible; } -BOOL LLTabContainer::selectTabByName(const std::string& name) +bool LLTabContainer::selectTabByName(const std::string& name) { LLPanel* panel = getPanelByName(name); if (!panel) { LL_WARNS() << "LLTabContainer::selectTabByName(" << name << ") failed" << LL_ENDL; - return FALSE; + return false; } - BOOL result = selectTabPanel(panel); + bool result = selectTabPanel(panel); return result; } -BOOL LLTabContainer::getTabPanelFlashing(LLPanel *child) +bool LLTabContainer::getTabPanelFlashing(LLPanel *child) { LLTabTuple* tuple = getTabByPanel(child); if( tuple ) { return tuple->mButton->getFlashing(); } - return FALSE; + return false; } -void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state ) +void LLTabContainer::setTabPanelFlashing(LLPanel* child, bool state ) { LLTabTuple* tuple = getTabByPanel(child); if( tuple ) @@ -1811,7 +1811,7 @@ void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel ) if (tuple) { - tuple->mTabPanel->setFocus(TRUE); + tuple->mTabPanel->setFocus(true); } } @@ -1821,7 +1821,7 @@ void LLTabContainer::onNextBtn( const LLSD& data ) { scrollNext(); } - mScrolled = FALSE; + mScrolled = false; if(mCurrentTabIdx < mTabList.size()-1) { @@ -1840,7 +1840,7 @@ void LLTabContainer::onNextBtnHeld( const LLSD& data ) { selectNextTab(); } - mScrolled = TRUE; + mScrolled = true; } } @@ -1850,7 +1850,7 @@ void LLTabContainer::onPrevBtn( const LLSD& data ) { scrollPrev(); } - mScrolled = FALSE; + mScrolled = false; if(mCurrentTabIdx > 0) { @@ -1879,7 +1879,7 @@ void LLTabContainer::onPrevBtnHeld( const LLSD& data ) { selectPrevTab(); } - mScrolled = TRUE; + mScrolled = true; } } @@ -2009,21 +2009,21 @@ void LLTabContainer::initButtons() } } - mPrevArrowBtn->setTabStop(FALSE); + mPrevArrowBtn->setTabStop(false); addChild(mPrevArrowBtn); - mNextArrowBtn->setTabStop(FALSE); + mNextArrowBtn->setTabStop(false); addChild(mNextArrowBtn); if (mJumpPrevArrowBtn) { - mJumpPrevArrowBtn->setTabStop(FALSE); + mJumpPrevArrowBtn->setTabStop(false); addChild(mJumpPrevArrowBtn); } if (mJumpNextArrowBtn) { - mJumpNextArrowBtn->setTabStop(FALSE); + mJumpNextArrowBtn->setTabStop(false); addChild(mJumpNextArrowBtn); } @@ -2088,7 +2088,7 @@ void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_p void LLTabContainer::updateMaxScrollPos() { static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - BOOL no_scroll = TRUE; + bool no_scroll = true; if (mIsVertical) { S32 tab_total_height = (BTN_HEIGHT + tabcntrv_pad) * getTabCount(); @@ -2099,7 +2099,7 @@ void LLTabContainer::updateMaxScrollPos() S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad) - mNextArrowBtn->getRect().mBottom; S32 additional_needed = tab_total_height - available_height_with_arrows; setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT + tabcntrv_pad) ) ); - no_scroll = FALSE; + no_scroll = false; } } else @@ -2131,7 +2131,7 @@ void LLTabContainer::updateMaxScrollPos() } // in case last tab doesn't actually fit on screen, make it the last scrolling position setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1)); - no_scroll = FALSE; + no_scroll = false; } } if (no_scroll) @@ -2197,9 +2197,9 @@ void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible ) } if( foundTab ) - this->setVisible( TRUE ); + this->setVisible( true ); else - this->setVisible( FALSE ); + this->setVisible( false ); updateMaxScrollPos(); } diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 1615d72758..d5fee27406 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -130,28 +130,28 @@ protected: public: //LLTabContainer( const std::string& name, const LLRect& rect, TabPosition pos, - // BOOL bordered, BOOL is_vertical); + // bool bordered, bool is_vertical); /*virtual*/ ~LLTabContainer(); // from LLView /*virtual*/ void setValue(const LLSD& value); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); /*virtual*/ void draw(); /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask ); /*virtual*/ bool handleMouseUp( S32 x, S32 y, MASK mask ); /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + /*virtual*/ bool handleKeyHere(KEY key, MASK mask); + /*virtual*/ bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType type, void* cargo_data, EAcceptance* accept, std::string& tooltip); - /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const; - /*virtual*/ LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const; + /*virtual*/ LLView* getChildView(const std::string& name, bool recurse = true) const; + /*virtual*/ LLView* findChildView(const std::string& name, bool recurse = true) const; /*virtual*/ void initFromParams(const LLPanel::Params& p); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); struct TabPanelParams : public LLInitParam::Block { @@ -181,7 +181,7 @@ public: void lockTabs(S32 num_tabs = 0); void unlockTabs(); S32 getNumLockedTabs() { return mLockedTabCount; } - void enableTabButton(S32 which, BOOL enable); + void enableTabButton(S32 which, bool enable); void deleteAllTabs(); LLPanel* getCurrentPanel(); S32 getCurrentPanelIndex(); @@ -197,13 +197,13 @@ public: void selectLastTab(); void selectNextTab(); void selectPrevTab(); - BOOL selectTabPanel( LLPanel* child ); - BOOL selectTab(S32 which); - BOOL selectTabByName(const std::string& title); + bool selectTabPanel( LLPanel* child ); + bool selectTab(S32 which); + bool selectTabByName(const std::string& title); void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; } - BOOL getTabPanelFlashing(LLPanel* child); - void setTabPanelFlashing(LLPanel* child, BOOL state); + bool getTabPanelFlashing(LLPanel* child); + void setTabPanelFlashing(LLPanel* child, bool state); void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white); void setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white); void setTabImage(LLPanel* child, LLIconCtrl* icon); @@ -238,7 +238,7 @@ private: void initButtons(); - BOOL setTab(S32 which); + bool setTab(S32 which); LLTabTuple* getTab(S32 index) { return mTabList[index]; } LLTabTuple* getTabByPanel(LLPanel* child); @@ -251,8 +251,8 @@ private: S32 getScrollPosPixels() const { return mScrollPosPixels; } void setScrollPosPixels(S32 pixels) { mScrollPosPixels = pixels; } - void setTabsHidden(BOOL hidden) { mTabsHidden = hidden; } - BOOL getTabsHidden() const { return mTabsHidden; } + void setTabsHidden(bool hidden) { mTabsHidden = hidden; } + bool getTabsHidden() const { return mTabsHidden; } void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap @@ -270,10 +270,10 @@ private: tuple_list_t mTabList; S32 mCurrentTabIdx; - BOOL mTabsHidden; - BOOL mHideScrollArrows; + bool mTabsHidden; + bool mHideScrollArrows; - BOOL mScrolled; + bool mScrolled; LLFrameTimer mScrollTimer; S32 mScrollPos; S32 mScrollPosPixels; @@ -288,7 +288,7 @@ private: LLButton* mPrevArrowBtn; LLButton* mNextArrowBtn; - BOOL mIsVertical; + bool mIsVertical; // Horizontal specific LLButton* mJumpPrevArrowBtn; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index d53e7154ba..65c57fc764 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -202,7 +202,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mSelectedBGColor(p.bg_selected_color), mReflowIndex(S32_MAX), mCursorPos( 0 ), - mScrollNeeded(FALSE), + mScrollNeeded(false), mDesiredXPixel(-1), mHPad(p.h_pad), mVPad(p.v_pad), @@ -218,7 +218,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mScrollIndex(-1), mSelectionStart( 0 ), mSelectionEnd( 0 ), - mIsSelecting( FALSE ), + mIsSelecting( false ), mPlainText ( p.plain_text ), mWordWrap(p.wrap), mUseEllipses( p.use_ellipses ), @@ -301,7 +301,7 @@ void LLTextBase::initFromParams(const LLTextBase::Params& p) bool LLTextBase::truncate() { - BOOL did_truncate = FALSE; + bool did_truncate = false; // First rough check - if we're less than 1/4th the size, we're OK if (getLength() >= S32(mMaxTextByteLength / 4)) @@ -328,7 +328,7 @@ bool LLTextBase::truncate() LLWString text = utf8str_to_wstring( temp_utf8_text ); // remove extra bit of current string, to preserve formatting, etc. removeStringNoUndo(text.size(), getWText().size() - text.size()); - did_truncate = TRUE; + did_truncate = true; } } @@ -1249,7 +1249,7 @@ bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask) } -void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent) { if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape) { @@ -1324,7 +1324,7 @@ void LLTextBase::draw() : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); - gl_rect_2d(text_rect, bg_color % alpha, TRUE); + gl_rect_2d(text_rect, bg_color % alpha, true); } // Draw highlighted if needed @@ -1335,7 +1335,7 @@ void LLTextBase::draw() if( mScroller ) bg_rect.intersectWith( text_rect ); - gl_rect_2d( text_rect, bg_color, TRUE ); + gl_rect_2d( text_rect, bg_color, true ); } bool should_clip = mClip || mScroller != NULL; @@ -1356,9 +1356,9 @@ void LLTextBase::draw() drawCursor(); } - mDocumentView->setVisibleDirect(FALSE); + mDocumentView->setVisibleDirect(false); LLUICtrl::draw(); - mDocumentView->setVisibleDirect(TRUE); + mDocumentView->setVisibleDirect(true); } @@ -1377,7 +1377,7 @@ void LLTextBase::setReadOnlyColor(const LLColor4 &c) } //virtual -void LLTextBase::onVisibilityChange( BOOL new_visibility ) +void LLTextBase::onVisibilityChange( bool new_visibility ) { LLContextMenu* menu = static_cast(mPopupMenuHandle.get()); if(!new_visibility && menu) @@ -1394,7 +1394,7 @@ void LLTextBase::setValue(const LLSD& value ) } //virtual -BOOL LLTextBase::canDeselect() const +bool LLTextBase::canDeselect() const { return hasSelection(); } @@ -1405,7 +1405,7 @@ void LLTextBase::deselect() { mSelectionStart = 0; mSelectionEnd = 0; - mIsSelecting = FALSE; + mIsSelecting = false; } bool LLTextBase::getSpellCheck() const @@ -1533,7 +1533,7 @@ void LLTextBase::updateScrollFromCursor() { return; } - mScrollNeeded = FALSE; + mScrollNeeded = false; // scroll so that the cursor is at the top of the page LLRect scroller_doc_window = getVisibleDocumentRect(); @@ -2038,7 +2038,7 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 i LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line) { // Find the cursor position at the requested local screen position - S32 offset = getDocIndexFromLocalCoord( x, y, FALSE, hit_past_end_of_line); + S32 offset = getDocIndexFromLocalCoord( x, y, false, hit_past_end_of_line); segment_set_t::iterator seg_iter = getSegIterContaining(offset); if (seg_iter != mSegments.end()) { @@ -2295,10 +2295,10 @@ void LLTextBase::setLabel(const LLStringExplicit& label) resetLabel(); } -BOOL LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text ) +bool LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text ) { mLabel.setArg(key, text); - return TRUE; + return true; } void LLTextBase::resetLabel() @@ -2381,10 +2381,10 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig // Save old state S32 selection_start = mSelectionStart; S32 selection_end = mSelectionEnd; - BOOL was_selecting = mIsSelecting; + bool was_selecting = mIsSelecting; S32 cursor_pos = mCursorPos; S32 old_length = getLength(); - BOOL cursor_was_at_end = (mCursorPos == old_length); + bool cursor_was_at_end = (mCursorPos == old_length); deselect(); @@ -2566,7 +2566,7 @@ const LLWString& LLTextBase::getWText() const // will be put to its right. If round is false, the cursor will always be put to the // character's left. -S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line) const +S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line) const { // Figure out which line we're nearest to. LLRect doc_rect = mDocumentView->getRect(); @@ -2846,7 +2846,7 @@ void LLTextBase::changeLine( S32 delta ) { LLRect visible_region = getVisibleDocumentRect(); S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, - mLineInfoList[new_line].mRect.mBottom + mVisibleTextRect.mBottom - visible_region.mBottom, TRUE); + mLineInfoList[new_line].mRect.mBottom + mVisibleTextRect.mBottom - visible_region.mBottom, true); S32 actual_line = getLineNumFromDocIndex(new_cursor_pos); if (actual_line != new_line) { @@ -3109,7 +3109,7 @@ void LLTextBase::startSelection() { if( !mIsSelecting ) { - mIsSelecting = TRUE; + mIsSelecting = true; mSelectionStart = mCursorPos; mSelectionEnd = mCursorPos; } @@ -3119,7 +3119,7 @@ void LLTextBase::endSelection() { if( mIsSelecting ) { - mIsSelecting = FALSE; + mIsSelecting = false; mSelectionEnd = mCursorPos; } } @@ -3267,7 +3267,7 @@ LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 e } } -LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) +LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) : LLTextSegment(start, end), mToken(NULL), mEditor(editor) @@ -3574,7 +3574,7 @@ LLLabelTextSegment::LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end { } -LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) +LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) : LLNormalTextSegment(color, start, end, editor, is_visible) { } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 1dd91eef32..236f97c4d0 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -125,7 +125,7 @@ class LLNormalTextSegment : public LLTextSegment { public: LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); - LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); + LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); virtual ~LLNormalTextSegment(); /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; @@ -138,7 +138,7 @@ public: /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; } /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; } /*virtual*/ LLKeywordToken* getToken() const { return mToken; } - /*virtual*/ BOOL getToolTip( std::string& msg ) const; + /*virtual*/ bool getToolTip( std::string& msg ) const; /*virtual*/ void setToolTip(const std::string& tooltip); /*virtual*/ void dump() const; @@ -170,7 +170,7 @@ class LLLabelTextSegment : public LLNormalTextSegment { public: LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); - LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); + LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); protected: @@ -351,20 +351,20 @@ public: /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); // LLView interface - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); /*virtual*/ void draw(); // LLUICtrl interface - /*virtual*/ BOOL acceptsTextInput() const { return !mReadOnly; } + /*virtual*/ bool acceptsTextInput() const { return !mReadOnly; } /*virtual*/ void setColor( const LLColor4& c ); virtual void setReadOnlyColor(const LLColor4 &c); - virtual void onVisibilityChange( BOOL new_visibility ); + virtual void onVisibilityChange( bool new_visibility ); /*virtual*/ void setValue(const LLSD& value ); /*virtual*/ LLTextViewModel* getViewModel() const; // LLEditMenuHandler interface - /*virtual*/ BOOL canDeselect() const; + /*virtual*/ bool canDeselect() const; /*virtual*/ void deselect(); virtual void onFocusReceived(); @@ -416,7 +416,7 @@ public: void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); void setLabel(const LLStringExplicit& label); - virtual BOOL setLabelArg(const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg(const std::string& key, const LLStringExplicit& text ); const std::string& getLabel() { return mLabel.getString(); } const LLWString& getWlabel() { return mLabel.getWString();} @@ -450,7 +450,7 @@ public: F32 getLineSpacingMult() { return mLineSpacingMult; } S32 getLineSpacingPixels() { return mLineSpacingPixels; } // only for multiline - S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line = true) const; + S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line = true) const; LLRect getLocalRectFromDocIndex(S32 pos) const; LLRect getDocRectFromDocIndex(S32 pos) const; @@ -525,7 +525,7 @@ protected: class TextCmd { public: - TextCmd( S32 pos, BOOL group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() ) + TextCmd( S32 pos, bool group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() ) : mPos(pos), mGroupWithNext(group_with_next) { @@ -535,13 +535,13 @@ protected: } } virtual ~TextCmd() {} - virtual BOOL execute(LLTextBase* editor, S32* delta) = 0; + virtual bool execute(LLTextBase* editor, S32* delta) = 0; virtual S32 undo(LLTextBase* editor) = 0; virtual S32 redo(LLTextBase* editor) = 0; - virtual BOOL canExtend(S32 pos) const { return FALSE; } + virtual bool canExtend(S32 pos) const { return false; } virtual void blockExtensions() {} - virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; } - virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; } + virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; } + virtual bool hasExtCharValue( llwchar value ) const { return false; } // Defined here so they can access protected LLTextEditor editing methods S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); } @@ -549,11 +549,11 @@ protected: S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); } S32 getPosition() const { return mPos; } - BOOL groupWithNext() const { return mGroupWithNext; } + bool groupWithNext() const { return mGroupWithNext; } protected: const S32 mPos; - BOOL mGroupWithNext; + bool mGroupWithNext; segment_vec_t mSegments; }; @@ -621,7 +621,7 @@ protected: // misc void updateRects(); - void needsScroll() { mScrollNeeded = TRUE; } + void needsScroll() { mScrollNeeded = true; } struct URLLabelCallback; // Replace a URL with a new icon and label, for example, when @@ -674,7 +674,7 @@ protected: S32 mSelectionEnd; LLTimer mTripleClickTimer; - BOOL mIsSelecting; // Are we in the middle of a drag-select? + bool mIsSelecting; // Are we in the middle of a drag-select? // spell checking bool mSpellCheck; diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 739b46bb07..f143d619c5 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -39,7 +39,7 @@ static LLDefaultChildRegistry::Register r("text"); // Compiler optimization, generate extern template template class LLTextBox* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; LLTextBox::LLTextBox(const LLTextBox::Params& p) : LLTextBase(p), @@ -115,7 +115,7 @@ bool LLTextBox::handleHover(S32 x, S32 y, MASK mask) return handled; } -void LLTextBox::setEnabled(BOOL enabled) +void LLTextBox::setEnabled(bool enabled) { // just treat enabled as read-only flag bool read_only = !enabled; @@ -156,16 +156,16 @@ LLSD LLTextBox::getValue() const return getViewModel()->getValue(); } -BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text ) +bool LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text ) { mText.setArg(key, text); LLTextBase::setText(mText.getString()); - return TRUE; + return true; } -void LLTextBox::reshapeToFitText(BOOL called_from_parent) +void LLTextBox::reshapeToFitText(bool called_from_parent) { reflow(); diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index bf0348723b..39016e2f4e 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -52,7 +52,7 @@ public: /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ void setEnabled(BOOL enabled); + /*virtual*/ void setEnabled(bool enabled); /*virtual*/ void setText( const LLStringExplicit& text, const LLStyle::Params& input_params = LLStyle::Params() ); @@ -60,13 +60,13 @@ public: void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } void setClickedCallback( boost::function cb, void* userdata = NULL ); - void reshapeToFitText(BOOL called_from_parent = FALSE); + void reshapeToFitText(bool called_from_parent = false); S32 getTextPixelWidth(); S32 getTextPixelHeight(); /*virtual*/ LLSD getValue() const; - /*virtual*/ BOOL setTextArg( const std::string& key, const LLStringExplicit& text ); + /*virtual*/ bool setTextArg( const std::string& key, const LLStringExplicit& text ); void setShowCursorHand(bool show_cursor) { mShowCursorHand = show_cursor; } @@ -81,7 +81,7 @@ protected: // Build time optimization, generate once in .cpp file #ifndef LLTEXTBOX_CPP extern template class LLTextBox* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif #endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index eabeeddcd4..2250ed5cb3 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -70,7 +70,7 @@ static LLDefaultChildRegistry::Register r("simple_text_editor"); // Compiler optimization, generate extern template template class LLTextEditor* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; // // Constants @@ -83,12 +83,12 @@ const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd { public: - TextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment) + TextCmdInsert(S32 pos, bool group_with_next, const LLWString &ws, LLTextSegmentPtr segment) : TextCmd(pos, group_with_next, segment), mWString(ws) { } virtual ~TextCmdInsert() {} - virtual BOOL execute( LLTextBase* editor, S32* delta ) + virtual bool execute( LLTextBase* editor, S32* delta ) { *delta = insert(editor, getPosition(), mWString ); LLWStringUtil::truncate(mWString, *delta); @@ -114,29 +114,29 @@ private: class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd { public: - TextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment) - : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE) + TextCmdAddChar( S32 pos, bool group_with_next, llwchar wc, LLTextSegmentPtr segment) + : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(false) { } virtual void blockExtensions() { - mBlockExtensions = TRUE; + mBlockExtensions = true; } - virtual BOOL canExtend(S32 pos) const + virtual bool canExtend(S32 pos) const { // cannot extend text with custom segments - if (!mSegments.empty()) return FALSE; + if (!mSegments.empty()) return false; return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length()); } - virtual BOOL execute( LLTextBase* editor, S32* delta ) + virtual bool execute( LLTextBase* editor, S32* delta ) { *delta = insert(editor, getPosition(), mWString); LLWStringUtil::truncate(mWString, *delta); //mWString = wstring_truncate(mWString, *delta); return (*delta != 0); } - virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta ) + virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta ) { LLWString ws; ws += wc; @@ -161,7 +161,7 @@ public: private: LLWString mWString; - BOOL mBlockExtensions; + bool mBlockExtensions; }; @@ -170,15 +170,15 @@ private: class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd { public: - TextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc) + TextCmdOverwriteChar( S32 pos, bool group_with_next, llwchar wc) : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} - virtual BOOL execute( LLTextBase* editor, S32* delta ) + virtual bool execute( LLTextBase* editor, S32* delta ) { mOldChar = editor->getWText()[getPosition()]; overwrite(editor, getPosition(), mChar); *delta = 0; - return TRUE; + return true; } virtual S32 undo( LLTextBase* editor ) { @@ -201,12 +201,12 @@ private: class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd { public: - TextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) : + TextCmdRemove( S32 pos, bool group_with_next, S32 len, segment_vec_t& segments ) : TextCmd(pos, group_with_next), mLen(len) { std::swap(mSegments, segments); } - virtual BOOL execute( LLTextBase* editor, S32* delta ) + virtual bool execute( LLTextBase* editor, S32* delta ) { mWString = editor->getWText().substr(getPosition(), mLen); *delta = remove(editor, getPosition(), mLen ); @@ -246,7 +246,7 @@ LLTextEditor::Params::Params() LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : LLTextBase(p), mAutoreplaceCallback(), - mBaseDocIsPristine(TRUE), + mBaseDocIsPristine(true), mPristineCmd( NULL ), mLastCmd( NULL ), mDefaultColor( p.default_color() ), @@ -259,7 +259,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mPrevalidateFunc(p.prevalidate_callback()), mShowContextMenu(p.show_context_menu), mEnableTooltipPaste(p.enable_tooltip_paste), - mPassDelete(FALSE), + mPassDelete(false), mKeepSelectionOnReturn(false) { mSourceID.generate(); @@ -275,7 +275,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : addChild( mBorder ); setText(p.default_text()); - mParseOnTheFly = TRUE; + mParseOnTheFly = true; } void LLTextEditor::initFromParams( const LLTextEditor::Params& p) @@ -329,14 +329,14 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Param blockUndo(); deselect(); - mParseOnTheFly = FALSE; + mParseOnTheFly = false; LLTextBase::setText(utf8str, input_params); - mParseOnTheFly = TRUE; + mParseOnTheFly = true; resetDirty(); } -void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap) +void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap) { if (search_text_in.empty()) { @@ -373,7 +373,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen // If still -1, then search_text just isn't found. if (-1 == loc) { - mIsSelecting = FALSE; + mIsSelecting = false; mSelectionEnd = 0; mSelectionStart = 0; return; @@ -381,15 +381,15 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen setCursorPos(loc); - mIsSelecting = TRUE; + mIsSelecting = true; mSelectionEnd = mCursorPos; mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size())); } -BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::string& replace_text, - BOOL case_insensitive, BOOL wrap) +bool LLTextEditor::replaceText(const std::string& search_text_in, const std::string& replace_text, + bool case_insensitive, bool wrap) { - BOOL replaced = FALSE; + bool replaced = false; if (search_text_in.empty()) { @@ -411,7 +411,7 @@ BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::str if (selected_text == search_text) { insertText(replace_text); - replaced = TRUE; + replaced = true; } } @@ -419,15 +419,15 @@ BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::str return replaced; } -void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive) +void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive) { startOfDoc(); - selectNext(search_text, case_insensitive, FALSE); + selectNext(search_text, case_insensitive, false); - BOOL replaced = TRUE; + bool replaced = true; while ( replaced ) { - replaced = replaceText(search_text,replace_text, case_insensitive, FALSE); + replaced = replaceText(search_text,replace_text, case_insensitive, false); } } @@ -505,7 +505,7 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, } } -BOOL LLTextEditor::selectionContainsLineBreaks() +bool LLTextEditor::selectionContainsLineBreaks() { if (hasSelection()) { @@ -517,11 +517,11 @@ BOOL LLTextEditor::selectionContainsLineBreaks() { if (wtext[i] == '\n') { - return TRUE; + return true; } } } - return FALSE; + return false; } @@ -552,7 +552,7 @@ S32 LLTextEditor::indentLine( S32 pos, S32 spaces ) LLWString wtext = getWText(); if (wtext[pos] == ' ') { - delta_spaces += remove( pos, 1, FALSE ); + delta_spaces += remove( pos, 1, false ); } } } @@ -567,7 +567,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces ) LLWString text = getWText(); S32 left = llmin( mSelectionStart, mSelectionEnd ); S32 right = left + llabs( mSelectionStart - mSelectionEnd ); - BOOL cursor_on_right = (mSelectionEnd > mSelectionStart); + bool cursor_on_right = (mSelectionEnd > mSelectionStart); S32 cur = left; // Expand left to start of line @@ -596,7 +596,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces ) // Disabling parsing on the fly to avoid updating text segments // until all indentation commands are executed. - mParseOnTheFly = FALSE; + mParseOnTheFly = false; // Find each start-of-line and indent it do @@ -623,7 +623,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces ) } while( cur < right ); - mParseOnTheFly = TRUE; + mParseOnTheFly = true; if( (right < getLength()) && (text[right] == '\n') ) { @@ -646,9 +646,9 @@ void LLTextEditor::indentSelectedLines( S32 spaces ) } //virtual -BOOL LLTextEditor::canSelectAll() const +bool LLTextEditor::canSelectAll() const { - return TRUE; + return true; } // virtual @@ -708,7 +708,7 @@ bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) mSelectionEnd = mCursorPos; } // assume we're starting a drag select - mIsSelecting = TRUE; + mIsSelecting = true; } else { @@ -977,7 +977,7 @@ S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) { S32 end_pos = getEditableIndex(pos + length, true); - BOOL removedChar = FALSE; + bool removedChar = false; segment_vec_t segments_to_remove; // store text segments @@ -999,7 +999,7 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) } else { - return execute(new TextCmdOverwriteChar(pos, FALSE, wc)); + return execute(new TextCmdOverwriteChar(pos, false, wc)); } } @@ -1044,7 +1044,7 @@ void LLTextEditor::removeCharOrTab() for (S32 i = 0; i < chars_to_remove; i++) { setCursorPos(mCursorPos - 1); - remove( mCursorPos, 1, FALSE ); + remove( mCursorPos, 1, false ); } } else @@ -1056,7 +1056,7 @@ void LLTextEditor::removeCharOrTab() // Remove a single character from the text S32 LLTextEditor::removeChar(S32 pos) { - return remove( pos, 1, FALSE ); + return remove( pos, 1, false ); } void LLTextEditor::removeChar() @@ -1107,7 +1107,7 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc) } else { - return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr())); + return execute(new TextCmdAddChar(pos, false, wc, LLTextSegmentPtr())); } } @@ -1119,7 +1119,7 @@ void LLTextEditor::addChar(llwchar wc) } if( hasSelection() ) { - deleteSelection(TRUE); + deleteSelection(true); } else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { @@ -1146,7 +1146,7 @@ void LLTextEditor::addChar(llwchar wc) } } -void LLTextEditor::addLineBreakChar(BOOL group_together) +void LLTextEditor::addLineBreakChar(bool group_together) { if( !getEnabled() ) { @@ -1154,7 +1154,7 @@ void LLTextEditor::addLineBreakChar(BOOL group_together) } if( hasSelection() ) { - deleteSelection(TRUE); + deleteSelection(true); } else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { @@ -1170,13 +1170,13 @@ void LLTextEditor::addLineBreakChar(BOOL group_together) } -BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) +bool LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( mask & MASK_SHIFT ) { - handled = TRUE; + handled = true; switch( key ) { @@ -1257,7 +1257,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) break; default: - handled = FALSE; + handled = false; break; } } @@ -1271,14 +1271,14 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) return handled; } -BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) +bool LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) { - BOOL handled = FALSE; + bool handled = false; // Ignore capslock key if( MASK_NONE == mask ) { - handled = TRUE; + handled = true; switch( key ) { case KEY_UP: @@ -1343,7 +1343,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) break; default: - handled = FALSE; + handled = false; break; } } @@ -1356,7 +1356,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) return handled; } -void LLTextEditor::deleteSelection(BOOL group_with_next_op ) +void LLTextEditor::deleteSelection(bool group_with_next_op ) { if( getEnabled() && hasSelection() ) { @@ -1371,7 +1371,7 @@ void LLTextEditor::deleteSelection(BOOL group_with_next_op ) } // virtual -BOOL LLTextEditor::canCut() const +bool LLTextEditor::canCut() const { return !mReadOnly && hasSelection(); } @@ -1386,12 +1386,12 @@ void LLTextEditor::cut() S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); LLClipboard::instance().copyToClipboard( getWText(), left_pos, length); - deleteSelection( FALSE ); + deleteSelection( false ); onKeyStroke(); } -BOOL LLTextEditor::canCopy() const +bool LLTextEditor::canCopy() const { return hasSelection(); } @@ -1408,7 +1408,7 @@ void LLTextEditor::copy() LLClipboard::instance().copyToClipboard(getWText(), left_pos, length); } -BOOL LLTextEditor::canPaste() const +bool LLTextEditor::canPaste() const { return !mReadOnly && LLClipboard::instance().isTextAvailable(); } @@ -1430,7 +1430,7 @@ void LLTextEditor::pastePrimary() // paste from primary (itsprimary==true) or clipboard (itsprimary==false) void LLTextEditor::pasteHelper(bool is_primary) { - mParseOnTheFly = FALSE; + mParseOnTheFly = false; bool can_paste_it; if (is_primary) { @@ -1457,7 +1457,7 @@ void LLTextEditor::pasteHelper(bool is_primary) // Delete any selected characters (the paste replaces them) if( (!is_primary) && hasSelection() ) { - deleteSelection(TRUE); + deleteSelection(true); } // Clean up string (replace tabs and remove characters that our fonts don't support). @@ -1472,7 +1472,7 @@ void LLTextEditor::pasteHelper(bool is_primary) deselect(); onKeyStroke(); - mParseOnTheFly = TRUE; + mParseOnTheFly = true; } @@ -1514,9 +1514,9 @@ void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string) if(pos!=start) { std::basic_string str = std::basic_string(clean_string,start,pos-start); - setCursorPos(mCursorPos + insert(mCursorPos, str, TRUE, LLTextSegmentPtr())); + setCursorPos(mCursorPos + insert(mCursorPos, str, true, LLTextSegmentPtr())); } - addLineBreakChar(TRUE); // Add a line break and group with the next addition. + addLineBreakChar(true); // Add a line break and group with the next addition. start = pos+1; pos = clean_string.find('\n',start); @@ -1525,11 +1525,11 @@ void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string) if (pos != start) { std::basic_string str = std::basic_string(clean_string,start,clean_string.length()-start); - setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr())); + setCursorPos(mCursorPos + insert(mCursorPos, str, false, LLTextSegmentPtr())); } else { - addLineBreakChar(FALSE); // Add a line break and end the grouping. + addLineBreakChar(false); // Add a line break and end the grouping. } } @@ -1545,7 +1545,7 @@ void LLTextEditor::copyPrimary() LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true); } -BOOL LLTextEditor::canPastePrimary() const +bool LLTextEditor::canPastePrimary() const { return !mReadOnly && LLClipboard::instance().isTextAvailable(true); } @@ -1558,13 +1558,13 @@ void LLTextEditor::updatePrimary() } } -BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask) +bool LLTextEditor::handleControlKey(const KEY key, const MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( mask & MASK_CONTROL ) { - handled = TRUE; + handled = true; switch( key ) { @@ -1628,7 +1628,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask) break; default: - handled = FALSE; + handled = false; break; } } @@ -1642,11 +1642,11 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask) } -BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) +bool LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) { - BOOL handled = TRUE; + bool handled = true; - if (mReadOnly) return FALSE; + if (mReadOnly) return false; switch( key ) { @@ -1660,7 +1660,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) case KEY_BACKSPACE: if( hasSelection() ) { - deleteSelection(FALSE); + deleteSelection(false); } else if( 0 < mCursorPos ) @@ -1679,7 +1679,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) { if( hasSelection() && !mKeepSelectionOnReturn ) { - deleteSelection(FALSE); + deleteSelection(false); } if (mAutoIndent) { @@ -1688,7 +1688,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) } else { - handled = FALSE; + handled = false; break; } break; @@ -1696,7 +1696,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) case KEY_TAB: if (mask & MASK_CONTROL) { - handled = FALSE; + handled = false; break; } if( hasSelection() && selectionContainsLineBreaks() ) @@ -1707,7 +1707,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) { if( hasSelection() ) { - deleteSelection(FALSE); + deleteSelection(false); } S32 offset = getLineOffsetFromDocIndex(mCursorPos); @@ -1721,7 +1721,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) break; default: - handled = FALSE; + handled = false; break; } @@ -1759,15 +1759,15 @@ void LLTextEditor::unindentLineBeforeCloseBrace() } -BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) +bool LLTextEditor::handleKeyHere(KEY key, MASK mask ) { - BOOL handled = FALSE; + bool handled = false; // Special case for TAB. If want to move to next field, report // not handled and let the parent take care of field movement. if (KEY_TAB == key && mTabsToNextField) { - return FALSE; + return false; } if (mReadOnly && mScroller) @@ -1791,7 +1791,7 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) // Delete any selected characters (the tooltip text replaces them) if(hasSelection()) { - deleteSelection(TRUE); + deleteSelection(true); } std::basic_string::size_type pos = tool_tip_text.find('\n',0); @@ -1803,7 +1803,7 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) // Add the text cleanStringForPaste(tool_tip_text); pasteTextWithLinebreaks(tool_tip_text); - handled = TRUE; + handled = true; } } else @@ -1866,7 +1866,7 @@ bool LLTextEditor::handleUnicodeCharHere(llwchar uni_char) // virtual -BOOL LLTextEditor::canDoDelete() const +bool LLTextEditor::canDoDelete() const { return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) ); } @@ -1879,7 +1879,7 @@ void LLTextEditor::doDelete() } if( hasSelection() ) { - deleteSelection(FALSE); + deleteSelection(false); } else if( mCursorPos < getLength() ) @@ -1923,14 +1923,14 @@ void LLTextEditor::doDelete() void LLTextEditor::blockUndo() { - mBaseDocIsPristine = FALSE; + mBaseDocIsPristine = false; mLastCmd = NULL; std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); mUndoStack.clear(); } // virtual -BOOL LLTextEditor::canUndo() const +bool LLTextEditor::canUndo() const { return !mReadOnly && mLastCmd != NULL; } @@ -1961,7 +1961,7 @@ void LLTextEditor::undo() onKeyStroke(); } -BOOL LLTextEditor::canRedo() const +bool LLTextEditor::canRedo() const { return !mReadOnly && (mUndoStack.size() > 0) && (mLastCmd != mUndoStack.front()); } @@ -2040,7 +2040,7 @@ void LLTextEditor::onCommit() LLTextBase::onCommit(); } -void LLTextEditor::setEnabled(BOOL enabled) +void LLTextEditor::setEnabled(bool enabled) { // just treat enabled as read-only flag bool read_only = !enabled; @@ -2073,7 +2073,7 @@ void LLTextEditor::showContextMenu(S32 x, S32 y) // Route menu to this class // previously this was done in ::handleRightMoseDown: //if(hasTabStop()) - // setFocus(TRUE) - why? weird... + // setFocus(true) - why? weird... // and then inside setFocus // .... // gEditMenuHandler = this; @@ -2251,9 +2251,9 @@ void LLTextEditor::draw() // Start or stop the editor from accepting text-editing keystrokes // see also LLLineEditor -void LLTextEditor::setFocus( BOOL new_state ) +void LLTextEditor::setFocus( bool new_state ) { - BOOL old_state = hasFocus(); + bool old_state = hasFocus(); // Don't change anything if the focus state didn't change if (new_state == old_state) return; @@ -2261,7 +2261,7 @@ void LLTextEditor::setFocus( BOOL new_state ) // Notify early if we are losing focus. if (!new_state) { - getWindow()->allowLanguageTextInput(this, FALSE); + getWindow()->allowLanguageTextInput(this, false); } LLTextBase::setFocus( new_state ); @@ -2293,7 +2293,7 @@ void LLTextEditor::setCursorAndScrollToEnd() endOfDoc(); } -void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap ) +void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap ) { *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap); *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap); @@ -2335,32 +2335,32 @@ void LLTextEditor::autoIndent() // Inserts new text at the cursor position void LLTextEditor::insertText(const std::string &new_text) { - BOOL enabled = getEnabled(); - setEnabled( TRUE ); + bool enabled = getEnabled(); + setEnabled( true ); // Delete any selected characters (the insertion replaces them) if( hasSelection() ) { - deleteSelection(TRUE); + deleteSelection(true); } - setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() )); + setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), false, LLTextSegmentPtr() )); setEnabled( enabled ); } void LLTextEditor::insertText(LLWString &new_text) { - BOOL enabled = getEnabled(); - setEnabled( TRUE ); + bool enabled = getEnabled(); + setEnabled( true ); // Delete any selected characters (the insertion replaces them) if( hasSelection() ) { - deleteSelection(TRUE); + deleteSelection(true); } - setCursorPos(mCursorPos + insert( mCursorPos, new_text, FALSE, LLTextSegmentPtr() )); + setCursorPos(mCursorPos + insert( mCursorPos, new_text, false, LLTextSegmentPtr() )); setEnabled( enabled ); } @@ -2370,10 +2370,10 @@ void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const // Save old state S32 selection_start = mSelectionStart; S32 selection_end = mSelectionEnd; - BOOL was_selecting = mIsSelecting; + bool was_selecting = mIsSelecting; S32 cursor_pos = mCursorPos; S32 old_length = getLength(); - BOOL cursor_was_at_end = (mCursorPos == old_length); + bool cursor_was_at_end = (mCursorPos == old_length); deselect(); @@ -2382,7 +2382,7 @@ void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const LLWString widget_wide_text = utf8str_to_wstring(text); LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size()); - insert(getLength(), widget_wide_text, FALSE, segment); + insert(getLength(), widget_wide_text, false, segment); // Set the cursor and scroll position if( selection_start != selection_end ) @@ -2412,7 +2412,7 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars) { if (num_chars <= 0) return; - remove(getLength() - num_chars, num_chars, FALSE); + remove(getLength() - num_chars, num_chars, false); S32 len = getLength(); setCursorPos (llclamp(mCursorPos, 0, len)); @@ -2428,7 +2428,7 @@ void LLTextEditor::onSpellCheckPerformed() { if (isPristine()) { - mBaseDocIsPristine = FALSE; + mBaseDocIsPristine = false; } } @@ -2445,7 +2445,7 @@ void LLTextEditor::makePristine() } } -BOOL LLTextEditor::isPristine() const +bool LLTextEditor::isPristine() const { if( mPristineCmd ) { @@ -2458,7 +2458,7 @@ BOOL LLTextEditor::isPristine() const } } -BOOL LLTextEditor::tryToRevertToPristineState() +bool LLTextEditor::tryToRevertToPristineState() { if( !isPristine() ) { @@ -2487,7 +2487,7 @@ BOOL LLTextEditor::tryToRevertToPristineState() } } - return isPristine(); // TRUE => success + return isPristine(); // true => success } void LLTextEditor::updateLinkSegments() @@ -2546,7 +2546,7 @@ void LLTextEditor::onMouseCaptureLost() /////////////////////////////////////////////////////////////////// // Hack for Notecards -BOOL LLTextEditor::importBuffer(const char* buffer, S32 length ) +bool LLTextEditor::importBuffer(const char* buffer, S32 length ) { std::istringstream instream(buffer); @@ -2565,20 +2565,20 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length ) if( 1 != sscanf(tbuf, "Linden text version %d", &version) ) { LL_WARNS() << "Invalid Linden text file header " << LL_ENDL; - return FALSE; + return false; } if( 1 != version ) { LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL; - return FALSE; + return false; } instream.getline(tbuf, MAX_STRING); if( 0 != sscanf(tbuf, "{") ) { LL_WARNS() << "Invalid Linden text file format" << LL_ENDL; - return FALSE; + return false; } S32 text_len = 0; @@ -2586,36 +2586,36 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length ) if( 1 != sscanf(tbuf, "Text length %d", &text_len) ) { LL_WARNS() << "Invalid Linden text length field" << LL_ENDL; - return FALSE; + return false; } if( text_len > mMaxTextByteLength ) { LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL; - return FALSE; + return false; } - BOOL success = TRUE; + bool success = true; char* text = new char[ text_len + 1]; if (text == NULL) { LL_ERRS() << "Memory allocation failure." << LL_ENDL; - return FALSE; + return false; } instream.get(text, text_len + 1, '\0'); text[text_len] = '\0'; if( text_len != (S32)strlen(text) )/* Flawfinder: ignore */ { LL_WARNS() << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << LL_ENDL;/* Flawfinder: ignore */ - success = FALSE; + success = false; } instream.getline(tbuf, MAX_STRING); if( success && (0 != sscanf(tbuf, "}")) ) { LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL; - success = FALSE; + success = false; } if( success ) @@ -2632,7 +2632,7 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length ) return success; } -BOOL LLTextEditor::exportBuffer(std::string &buffer ) +bool LLTextEditor::exportBuffer(std::string &buffer ) { std::ostringstream outstream(buffer); @@ -2643,7 +2643,7 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer ) outstream << getText(); outstream << "}\n"; - return TRUE; + return true; } void LLTextEditor::updateAllowingLanguageInput() @@ -2656,17 +2656,17 @@ void LLTextEditor::updateAllowingLanguageInput() } if (hasFocus() && !mReadOnly) { - window->allowLanguageTextInput(this, TRUE); + window->allowLanguageTextInput(this, true); } else { - window->allowLanguageTextInput(this, FALSE); + window->allowLanguageTextInput(this, false); } } // Preedit is managed off the undo/redo command stack. -BOOL LLTextEditor::hasPreeditString() const +bool LLTextEditor::hasPreeditString() const { return (mPreeditPositions.size() > 1); } @@ -2682,7 +2682,7 @@ void LLTextEditor::resetPreedit() } else { - deleteSelection(TRUE); + deleteSelection(true); } } if (hasPreeditString()) @@ -2913,11 +2913,11 @@ S32 LLTextEditor::getPreeditFontSize() const return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); } -BOOL LLTextEditor::isDirty() const +bool LLTextEditor::isDirty() const { if(mReadOnly) { - return FALSE; + return false; } if( mPristineCmd ) diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 26d61b9502..b96a433d5d 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -87,7 +87,7 @@ public: void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback); - void setParseHighlights(BOOL parsing) {mParseHighlights=parsing;} + void setParseHighlights(bool parsing) {mParseHighlights=parsing;} static S32 spacesPerTab(); @@ -99,7 +99,7 @@ public: virtual bool handleDoubleClick(S32 x, S32 y, MASK mask ); virtual bool handleMiddleMouseDown(S32 x,S32 y,MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask ); + virtual bool handleKeyHere(KEY key, MASK mask ); virtual bool handleUnicodeCharHere(llwchar uni_char); virtual void onMouseCaptureLost(); @@ -109,51 +109,51 @@ public: virtual void onFocusReceived(); virtual void onFocusLost(); virtual void onCommit(); - virtual void setEnabled(BOOL enabled); + virtual void setEnabled(bool enabled); // uictrl overrides virtual void clear(); - virtual void setFocus( BOOL b ); - virtual BOOL isDirty() const; + virtual void setFocus( bool b ); + virtual bool isDirty() const; // LLEditMenuHandler interface virtual void undo(); - virtual BOOL canUndo() const; + virtual bool canUndo() const; virtual void redo(); - virtual BOOL canRedo() const; + virtual bool canRedo() const; virtual void cut(); - virtual BOOL canCut() const; + virtual bool canCut() const; virtual void copy(); - virtual BOOL canCopy() const; + virtual bool canCopy() const; virtual void paste(); - virtual BOOL canPaste() const; + virtual bool canPaste() const; virtual void updatePrimary(); virtual void copyPrimary(); virtual void pastePrimary(); - virtual BOOL canPastePrimary() const; + virtual bool canPastePrimary() const; virtual void doDelete(); - virtual BOOL canDoDelete() const; + virtual bool canDoDelete() const; virtual void selectAll(); - virtual BOOL canSelectAll() const; + virtual bool canSelectAll() const; void selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos); virtual bool canLoadOrSaveToFile(); - void selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE); - BOOL replaceText(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive, BOOL wrap = TRUE); - void replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive); + void selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap = true); + bool replaceText(const std::string& search_text, const std::string& replace_text, bool case_insensitive, bool wrap = true); + void replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive); // Undo/redo stack void blockUndo(); // Text editing virtual void makePristine(); - BOOL isPristine() const; - BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; } + bool isPristine() const; + bool allowsEmbeddedItems() const { return mAllowEmbeddedItems; } // Autoreplace (formerly part of LLLineEditor) typedef boost::function autoreplace_callback_t; @@ -179,19 +179,19 @@ public: // Does not change highlight or cursor position. void removeTextFromEnd(S32 num_chars); - BOOL tryToRevertToPristineState(); + bool tryToRevertToPristineState(); void setCursorAndScrollToEnd(); - void getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap ); + void getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap ); // Hacky methods to make it into a word-wrapping, potentially scrolling, // read-only text box. - void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; } + void setCommitOnFocusLost(bool b) { mCommitOnFocusLost = b; } // Hack to handle Notecards - virtual BOOL importBuffer(const char* buffer, S32 length ); - virtual BOOL exportBuffer(std::string& buffer ); + virtual bool importBuffer(const char* buffer, S32 length ); + virtual bool exportBuffer(std::string& buffer ); const LLUUID& getSourceID() const { return mSourceID; } @@ -202,7 +202,7 @@ public: void setShowContextMenu(bool show) { mShowContextMenu = show; } bool getShowContextMenu() const { return mShowContextMenu; } - void setPassDelete(BOOL b) { mPassDelete = b; } + void setPassDelete(bool b) { mPassDelete = b; } protected: void showContextMenu(S32 x, S32 y); @@ -216,13 +216,13 @@ protected: S32 indentLine( S32 pos, S32 spaces ); void unindentLineBeforeCloseBrace(); - virtual BOOL handleSpecialKey(const KEY key, const MASK mask); - BOOL handleNavigationKey(const KEY key, const MASK mask); - BOOL handleSelectionKey(const KEY key, const MASK mask); - BOOL handleControlKey(const KEY key, const MASK mask); + virtual bool handleSpecialKey(const KEY key, const MASK mask); + bool handleNavigationKey(const KEY key, const MASK mask); + bool handleSelectionKey(const KEY key, const MASK mask); + bool handleControlKey(const KEY key, const MASK mask); - BOOL selectionContainsLineBreaks(); - void deleteSelection(BOOL transient_operation); + bool selectionContainsLineBreaks(); + void deleteSelection(bool transient_operation); S32 prevWordPos(S32 cursorPos) const; S32 nextWordPos(S32 cursorPos) const; @@ -241,7 +241,7 @@ protected: // Undoable operations void addChar(llwchar c); // at mCursorPos S32 addChar(S32 pos, llwchar wc); - void addLineBreakChar(BOOL group_together = FALSE); + void addLineBreakChar(bool group_together = false); S32 overwriteChar(S32 pos, llwchar wc); void removeChar(); S32 removeChar(S32 pos); @@ -250,7 +250,7 @@ protected: void focusLostHelper(); void updateAllowingLanguageInput(); - BOOL hasPreeditString() const; + bool hasPreeditString() const; // Overrides LLPreeditor virtual void resetPreedit(); @@ -304,7 +304,7 @@ private: class TextCmdOverwriteChar; class TextCmdRemove; - BOOL mBaseDocIsPristine; + bool mBaseDocIsPristine; TextCmd* mPristineCmd; TextCmd* mLastCmd; @@ -312,11 +312,11 @@ private: typedef std::deque undo_stack_t; undo_stack_t mUndoStack; - BOOL mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces - BOOL mCommitOnFocusLost; - BOOL mTakesFocus; + bool mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces + bool mCommitOnFocusLost; + bool mTakesFocus; - BOOL mAllowEmbeddedItems; + bool mAllowEmbeddedItems; bool mShowContextMenu; bool mEnableTooltipPaste; bool mPassDelete; @@ -335,7 +335,7 @@ private: // Build time optimization, generate once in .cpp file #ifndef LLTEXTEDITOR_CPP extern template class LLTextEditor* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif #endif // LL_TEXTEDITOR_H diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp index 0b36241da0..e9fc6592d0 100644 --- a/indra/llui/lltextparser.cpp +++ b/indra/llui/lltextparser.cpp @@ -184,11 +184,11 @@ bool LLTextParser::parseFullLineHighlights(const std::string &text, LLColor4 *co { LLSD color_llsd = mHighlights[i]["color"]; color->setValue(color_llsd); - return TRUE; + return true; } } } - return FALSE; //No matches found. + return false; //No matches found. } std::string LLTextParser::getFileName() @@ -229,11 +229,11 @@ bool LLTextParser::saveToDisk(LLSD highlights) if (filename.empty()) { LL_WARNS() << "LLTextParser::saveToDisk() no valid user directory." << LL_ENDL; - return FALSE; + return false; } llofstream file; file.open(filename.c_str()); LLSDSerialize::toPrettyXML(mHighlights, file); file.close(); - return TRUE; + return true; } diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp index bfe0a5bb5d..bd3cfbacde 100644 --- a/indra/llui/lltextvalidate.cpp +++ b/indra/llui/lltextvalidate.cpp @@ -55,7 +55,7 @@ namespace LLTextValidate { LLLocale locale(LLLocale::USER_LOCALE); - bool success = TRUE; + bool success = true; LLWString trimmed = str; LLWStringUtil::trim(trimmed); S32 len = trimmed.length(); @@ -76,7 +76,7 @@ namespace LLTextValidate { if( (decimal_point != trimmed[i] ) && !LLStringOps::isDigit( trimmed[i] ) ) { - success = FALSE; + success = false; break; } } @@ -93,7 +93,7 @@ namespace LLTextValidate { LLLocale locale(LLLocale::USER_LOCALE); - bool success = TRUE; + bool success = true; LLWString trimmed = str; LLWStringUtil::trim(trimmed); S32 len = trimmed.length(); @@ -111,7 +111,7 @@ namespace LLTextValidate { if( !LLStringOps::isDigit( trimmed[i] ) ) { - success = FALSE; + success = false; break; } } @@ -127,19 +127,19 @@ namespace LLTextValidate LLWString trimmed = str; LLWStringUtil::trim(trimmed); S32 len = trimmed.length(); - bool success = TRUE; + bool success = true; if(0 < len) { if(('-' == trimmed[0]) || ('0' == trimmed[0])) { - success = FALSE; + success = false; } S32 i = 0; while(success && (i < len)) { if(!LLStringOps::isDigit(trimmed[i++])) { - success = FALSE; + success = false; } } } @@ -148,7 +148,7 @@ namespace LLTextValidate S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10); if (val <= 0) { - success = FALSE; + success = false; } } return success; @@ -161,19 +161,19 @@ namespace LLTextValidate LLWString trimmed = str; LLWStringUtil::trim(trimmed); S32 len = trimmed.length(); - bool success = TRUE; + bool success = true; if(0 < len) { if('-' == trimmed[0]) { - success = FALSE; + success = false; } S32 i = 0; while(success && (i < len)) { if(!LLStringOps::isDigit(trimmed[i++])) { - success = FALSE; + success = false; } } } @@ -182,7 +182,7 @@ namespace LLTextValidate S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10); if (val < 0) { - success = FALSE; + success = false; } } return success; @@ -194,19 +194,19 @@ namespace LLTextValidate LLWString test_str = str; S32 len = test_str.length(); - bool success = TRUE; + bool success = true; if(0 < len) { if('-' == test_str[0]) { - success = FALSE; + success = false; } S32 i = 0; while(success && (i < len)) { if(!LLStringOps::isDigit(test_str[i]) || LLStringOps::isSpace(test_str[i++])) { - success = FALSE; + success = false; } } } @@ -215,7 +215,7 @@ namespace LLTextValidate S32 val = strtol(wstring_to_utf8str(test_str).c_str(), NULL, 10); if (val < 0) { - success = FALSE; + success = false; } } return success; @@ -225,14 +225,14 @@ namespace LLTextValidate { LLLocale locale(LLLocale::USER_LOCALE); - bool rv = TRUE; + bool rv = true; S32 len = str.length(); if(len == 0) return rv; while(len--) { if( !LLStringOps::isAlnum((char)str[len]) ) { - rv = FALSE; + rv = false; break; } } @@ -243,14 +243,14 @@ namespace LLTextValidate { LLLocale locale(LLLocale::USER_LOCALE); - bool rv = TRUE; + bool rv = true; S32 len = str.length(); if(len == 0) return rv; while(len--) { if(!(LLStringOps::isAlnum((char)str[len]) || (' ' == str[len]))) { - rv = FALSE; + rv = false; break; } } @@ -262,7 +262,7 @@ namespace LLTextValidate // inventory item names, parcel names, object names, etc. bool validateASCIIPrintableNoPipe(const LLWString &str) { - bool rv = TRUE; + bool rv = true; S32 len = str.length(); if(len == 0) return rv; while(len--) @@ -272,14 +272,14 @@ namespace LLTextValidate || wc > 0x7f || wc == '|') { - rv = FALSE; + rv = false; break; } if(!(wc == ' ' || LLStringOps::isAlnum((char)wc) || LLStringOps::isPunct((char)wc) ) ) { - rv = FALSE; + rv = false; break; } } @@ -290,7 +290,7 @@ namespace LLTextValidate // Used for avatar names bool validateASCIIPrintableNoSpace(const LLWString &str) { - bool rv = TRUE; + bool rv = true; S32 len = str.length(); if(len == 0) return rv; while(len--) @@ -300,13 +300,13 @@ namespace LLTextValidate || wc > 0x7f || LLStringOps::isSpace(wc)) { - rv = FALSE; + rv = false; break; } if( !(LLStringOps::isAlnum((char)str[len]) || LLStringOps::isPunct((char)str[len]) ) ) { - rv = FALSE; + rv = false; break; } } @@ -315,13 +315,13 @@ namespace LLTextValidate bool validateASCII(const LLWString &str) { - bool rv = TRUE; + bool rv = true; S32 len = str.length(); while(len--) { if (str[len] < 0x20 || str[len] > 0x7f) { - rv = FALSE; + rv = false; break; } } @@ -332,7 +332,7 @@ namespace LLTextValidate { if (LLStringOps::isSpace(str[0])) { - return FALSE; + return false; } return validateASCII(str); } @@ -341,13 +341,13 @@ namespace LLTextValidate // Example is landmark description in Places SP. bool validateASCIIWithNewLine(const LLWString &str) { - bool rv = TRUE; + bool rv = true; S32 len = str.length(); while(len--) { if ((str[len] < 0x20 && str[len] != 0xA) || str[len] > 0x7f) { - rv = FALSE; + rv = false; break; } } diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h index e2b6c313d6..57d7419c8c 100644 --- a/indra/llui/lltextvalidate.h +++ b/indra/llui/lltextvalidate.h @@ -34,7 +34,7 @@ namespace LLTextValidate { - typedef boost::function validate_func_t; + typedef boost::function validate_func_t; struct ValidateTextNamedFuncs : public LLInitParam::TypeValuesHelper diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp index 516057f8fd..37cc52f967 100644 --- a/indra/llui/lltimectrl.cpp +++ b/indra/llui/lltimectrl.cpp @@ -131,7 +131,7 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p) mDownBtn = LLUICtrlFactory::create(down_button_params); addChild(mDownBtn); - setUseBoundingRect( TRUE ); + setUseBoundingRect( true ); } F32 LLTimeCtrl::getTime24() const @@ -158,27 +158,27 @@ void LLTimeCtrl::setTime24(F32 time) updateText(); } -BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask) +bool LLTimeCtrl::handleKeyHere(KEY key, MASK mask) { if (mEditor->hasFocus()) { if(key == KEY_UP) { onUpBtn(); - return TRUE; + return true; } if(key == KEY_DOWN) { onDownBtn(); - return TRUE; + return true; } if (key == KEY_RETURN) { onCommit(); - return TRUE; + return true; } } - return FALSE; + return false; } void LLTimeCtrl::onUpBtn() diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h index b5f268c76a..3fac65039a 100644 --- a/indra/llui/lltimectrl.h +++ b/indra/llui/lltimectrl.h @@ -81,7 +81,7 @@ private: }; virtual void onFocusLost(); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); void onUpBtn(); void onDownBtn(); @@ -126,6 +126,6 @@ private: U32 mTime; // minutes since midnight: 0 - 1439 U32 mSnapToMin; // interval in minutes to snap to - BOOL mAllowEdit; + bool mAllowEdit; }; #endif /* LLTIMECTRL_H_ */ diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp index 3e56e0a589..b324eff132 100644 --- a/indra/llui/lltoggleablemenu.cpp +++ b/indra/llui/lltoggleablemenu.cpp @@ -52,7 +52,7 @@ boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const } // virtual -void LLToggleableMenu::onVisibilityChange (BOOL curVisibilityIn) +void LLToggleableMenu::onVisibilityChange (bool curVisibilityIn) { S32 x,y; LLUI::getInstance()->getMousePositionLocal(LLUI::getInstance()->getRootView(), &x, &y); @@ -94,7 +94,7 @@ bool LLToggleableMenu::toggleVisibility() if (getVisible()) { - setVisible(FALSE); + setVisible(false); mClosedByButtonClick = false; return false; } diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h index 55a6483021..b1260f050f 100644 --- a/indra/llui/lltoggleablemenu.h +++ b/indra/llui/lltoggleablemenu.h @@ -45,7 +45,7 @@ public: boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb ); - virtual void onVisibilityChange (BOOL curVisibilityIn); + virtual void onVisibilityChange (bool curVisibilityIn); virtual bool addChild (LLView* view, S32 tab_group = 0); diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp index 7925c1048d..580d91fb4b 100644 --- a/indra/llui/lltoolbar.cpp +++ b/indra/llui/lltoolbar.cpp @@ -399,7 +399,7 @@ bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash, bool forc if (it != mButtonMap.end()) { command_button = it->second; - command_button->setFlashing((BOOL)(flash),(BOOL)(force_flashing)); + command_button->setFlashing((bool)(flash),(bool)(force_flashing)); } } @@ -444,9 +444,9 @@ bool LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask) return handle_it_here; } -BOOL LLToolBar::isSettingChecked(const LLSD& userdata) +bool LLToolBar::isSettingChecked(const LLSD& userdata) { - BOOL retval = FALSE; + bool retval = false; const std::string setting_name = userdata.asString(); @@ -785,8 +785,8 @@ void LLToolBar::updateLayoutAsNeeded() if (!mButtons.empty()) { - mButtonPanel->setVisible(TRUE); - mButtonPanel->setMouseOpaque(TRUE); + mButtonPanel->setVisible(true); + mButtonPanel->setMouseOpaque(true); } // don't clear flag until after we've resized ourselves, to avoid laying out every frame @@ -798,13 +798,13 @@ void LLToolBar::draw() { if (mButtons.empty()) { - mButtonPanel->setVisible(FALSE); - mButtonPanel->setMouseOpaque(FALSE); + mButtonPanel->setVisible(false); + mButtonPanel->setMouseOpaque(false); } else { - mButtonPanel->setVisible(TRUE); - mButtonPanel->setMouseOpaque(TRUE); + mButtonPanel->setVisible(true); + mButtonPanel->setMouseOpaque(true); } // Update enable/disable state and highlight state for editable toolbars @@ -842,7 +842,7 @@ void LLToolBar::draw() } LLIconCtrl* caret = mCaretIcon; - caret->setVisible(FALSE); + caret->setVisible(false); if (mDragAndDropTarget && !mButtonCommands.empty()) { LLRect caret_rect = caret->getRect(); @@ -860,15 +860,15 @@ void LLToolBar::draw() mDragx+mDragGirth, mDragy-caret_rect.getHeight()/2)); } - caret->setVisible(TRUE); + caret->setVisible(true); } LLUICtrl::draw(); - caret->setVisible(FALSE); + caret->setVisible(false); mDragAndDropTarget = false; } -void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLToolBar::reshape(S32 width, S32 height, bool called_from_parent) { LLUICtrl::reshape(width, height, called_from_parent); mNeedsLayout = true; @@ -1043,14 +1043,14 @@ boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_sign return connectSignal(mButtonRemoveSignal, cb); } -BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) { // If we have a drop callback, that means that we can handle the drop - BOOL handled = (mHandleDropCallback ? TRUE : FALSE); + bool handled = (mHandleDropCallback ? true : false); // if drop is set, it's time to call the callback to get the operation done if (handled && drop) @@ -1084,7 +1084,7 @@ BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, } else { - handled = FALSE; + handled = false; } } @@ -1165,7 +1165,7 @@ void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask) // Always highlight toolbar buttons, even if they are disabled if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this) { - mNeedsHighlight = TRUE; + mNeedsHighlight = true; } LLToolBar* parent_toolbar = getParentByType(); @@ -1201,12 +1201,12 @@ void LLToolBarButton::onCommit() } } -void LLToolBarButton::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLToolBarButton::reshape(S32 width, S32 height, bool called_from_parent) { LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent); } -void LLToolBarButton::setEnabled(BOOL enabled) +void LLToolBarButton::setEnabled(bool enabled) { if (enabled) { diff --git a/indra/llui/lltoolbar.h b/indra/llui/lltoolbar.h index 3a4f2fb76e..0f5f7b16b7 100644 --- a/indra/llui/lltoolbar.h +++ b/indra/llui/lltoolbar.h @@ -40,8 +40,8 @@ class LLToolBarButton; class LLIconCtrl; typedef boost::function tool_startdrag_callback_t; -typedef boost::function tool_handledrag_callback_t; -typedef boost::function tool_handledrop_callback_t; +typedef boost::function tool_handledrag_callback_t; +typedef boost::function tool_handledrop_callback_t; class LLToolBarButton : public LLButton { @@ -65,8 +65,8 @@ public: bool handleMouseDown(S32 x, S32 y, MASK mask); bool handleHover(S32 x, S32 y, MASK mask); - void reshape(S32 width, S32 height, BOOL called_from_parent = true); - void setEnabled(BOOL enabled); + void reshape(S32 width, S32 height, bool called_from_parent = true); + void setEnabled(bool enabled); void setCommandId(const LLCommandId& id) { mId = id; } LLCommandId getCommandId() { return mId; } @@ -214,9 +214,9 @@ public: // virtuals void draw(); - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + void reshape(S32 width, S32 height, bool called_from_parent = true); bool handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -270,7 +270,7 @@ private: void updateLayoutAsNeeded(); void createButtons(); void resizeButtonsInRow(std::vector& buttons_in_row, S32 max_row_girth); - BOOL isSettingChecked(const LLSD& userdata); + bool isSettingChecked(const LLSD& userdata); void onSettingEnable(const LLSD& userdata); void onRemoveSelectedCommand(); diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 0c5a1adcb3..2cd50a7a39 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -321,14 +321,14 @@ void LLToolTip::snapToChildren() setShape(tooltip_rect); } -void LLToolTip::setVisible(BOOL visible) +void LLToolTip::setVisible(bool visible) { // fade out tooltip over time if (visible) { mVisibleTimer.start(); mFadeTimer.stop(); - LLPanel::setVisible(TRUE); + LLPanel::setVisible(true); } else { @@ -539,7 +539,7 @@ void LLToolTipMgr::hideToolTips() { if (mToolTip) { - mToolTip->setVisible(FALSE); + mToolTip->setVisible(false); } } diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h index e95a1754d0..309d2a468e 100644 --- a/indra/llui/lltooltip.h +++ b/indra/llui/lltooltip.h @@ -99,7 +99,7 @@ public: /*virtual*/ void draw(); /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); - /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setVisible(bool visible); bool isFading(); F32 getVisibleTime(); diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp index 5da722a72b..05e1ef35ed 100644 --- a/indra/llui/lltransutil.cpp +++ b/indra/llui/lltransutil.cpp @@ -56,7 +56,7 @@ bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; LLUICtrl::CallbackParam::CallbackParam() : name("name"), @@ -100,10 +100,10 @@ const LLUICtrl::Params& LLUICtrl::getDefaultParams() LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) : LLView(p), - mIsChrome(FALSE), + mIsChrome(false), mRequestsFront(p.requests_front), - mTabStop(FALSE), - mTentative(FALSE), + mTabStop(false), + mTentative(false), mViewModel(viewmodel), mControlVariable(NULL), mEnabledControlVariable(NULL), @@ -414,7 +414,7 @@ bool LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask) } // can't tab to children of a non-tab-stop widget -BOOL LLUICtrl::canFocusChildren() const +bool LLUICtrl::canFocusChildren() const { return hasTabStop(); } @@ -439,9 +439,9 @@ void LLUICtrl::onCommit() } //virtual -BOOL LLUICtrl::isCtrl() const +bool LLUICtrl::isCtrl() const { - return TRUE; + return true; } //virtual @@ -473,7 +473,7 @@ LLViewModel* LLUICtrl::getViewModel() const } //virtual -BOOL LLUICtrl::postBuild() +bool LLUICtrl::postBuild() { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; // @@ -662,15 +662,15 @@ bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle handle, } // virtual -BOOL LLUICtrl::setTextArg( const std::string& key, const LLStringExplicit& text ) +bool LLUICtrl::setTextArg( const std::string& key, const LLStringExplicit& text ) { - return FALSE; + return false; } // virtual -BOOL LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) { - return FALSE; + return false; } // virtual @@ -691,12 +691,12 @@ LLCtrlScrollInterface* LLUICtrl::getScrollInterface() return NULL; } -BOOL LLUICtrl::hasFocus() const +bool LLUICtrl::hasFocus() const { return (gFocusMgr.childHasKeyboardFocus(this)); } -void LLUICtrl::setFocus(BOOL b) +void LLUICtrl::setFocus(bool b) { // focus NEVER goes to ui ctrls that are disabled! if (!getEnabled()) @@ -720,25 +720,25 @@ void LLUICtrl::setFocus(BOOL b) } // virtual -void LLUICtrl::setTabStop( BOOL b ) +void LLUICtrl::setTabStop( bool b ) { mTabStop = b; } // virtual -BOOL LLUICtrl::hasTabStop() const +bool LLUICtrl::hasTabStop() const { return mTabStop; } // virtual -BOOL LLUICtrl::acceptsTextInput() const +bool LLUICtrl::acceptsTextInput() const { - return FALSE; + return false; } //virtual -BOOL LLUICtrl::isDirty() const +bool LLUICtrl::isDirty() const { return mViewModel->isDirty(); }; @@ -761,13 +761,13 @@ void LLUICtrl::clear() } // virtual -void LLUICtrl::setIsChrome(BOOL is_chrome) +void LLUICtrl::setIsChrome(bool is_chrome) { mIsChrome = is_chrome; } // virtual -BOOL LLUICtrl::getIsChrome() const +bool LLUICtrl::getIsChrome() const { LLView* parent_ctrl = getParent(); while(parent_ctrl) @@ -790,7 +790,7 @@ BOOL LLUICtrl::getIsChrome() const } -BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) +bool LLUICtrl::focusFirstItem(bool prefer_text_fields, bool focus_flash) { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; // try to select default tab group child @@ -801,14 +801,14 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) LLUICtrl * ctrl = static_cast(result.back()); if(!ctrl->hasFocus()) { - ctrl->setFocus(TRUE); + ctrl->setFocus(true); ctrl->onTabInto(); if(focus_flash) { gFocusMgr.triggerFocusFlash(); } } - return TRUE; + return true; } // search for text field first if(prefer_text_fields) @@ -821,14 +821,14 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) LLUICtrl * ctrl = static_cast(result.back()); if(!ctrl->hasFocus()) { - ctrl->setFocus(TRUE); + ctrl->setFocus(true); ctrl->onTabInto(); if(focus_flash) { gFocusMgr.triggerFocusFlash(); } } - return TRUE; + return true; } } // no text field found, or we don't care about text fields @@ -838,20 +838,20 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) LLUICtrl * ctrl = static_cast(result.back()); if(!ctrl->hasFocus()) { - ctrl->setFocus(TRUE); + ctrl->setFocus(true); ctrl->onTabInto(); if(focus_flash) { gFocusMgr.triggerFocusFlash(); } } - return TRUE; + return true; } - return FALSE; + return false; } -BOOL LLUICtrl::focusNextItem(BOOL text_fields_only) +bool LLUICtrl::focusNextItem(bool text_fields_only) { // this assumes that this method is called on the focus root. LLViewQuery query = getTabOrderQuery(); @@ -864,7 +864,7 @@ BOOL LLUICtrl::focusNextItem(BOOL text_fields_only) return focusNext(result); } -BOOL LLUICtrl::focusPrevItem(BOOL text_fields_only) +bool LLUICtrl::focusPrevItem(bool text_fields_only) { // this assumes that this method is called on the focus root. LLViewQuery query = getTabOrderQuery(); @@ -1016,13 +1016,13 @@ boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function getHandle() const { return getDerivedHandle(); } - BOOL getIsChrome() const; + bool getIsChrome() const; - void setTabStop( BOOL b ); - BOOL hasTabStop() const; + void setTabStop( bool b ); + bool hasTabStop() const; LLUICtrl* getParentUICtrl() const; @@ -266,7 +266,7 @@ public: LLSINGLETON_EMPTY_CTOR(LLTextInputFilter); /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(view->isCtrl() && static_cast(view)->acceptsTextInput(), TRUE); + return filterResult_t(view->isCtrl() && static_cast(view)->acceptsTextInput(), true); } }; @@ -322,10 +322,10 @@ protected: private: - BOOL mIsChrome; - BOOL mRequestsFront; - BOOL mTabStop; - BOOL mTentative; + bool mIsChrome; + bool mRequestsFront; + bool mTabStop; + bool mTentative; ETypeTransparency mTransparencyType; }; @@ -333,7 +333,7 @@ private: // Build time optimization, generate once in .cpp file #ifndef LLUICTRL_CPP extern template class LLUICtrl* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif #endif // LL_LLUICTRL_H diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index a85db17c7f..dca777cc1f 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -124,7 +124,7 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid LLXMLNodePtr outputChild; if (output_node) { - outputChild = output_node->createChild("", FALSE); + outputChild = output_node->createChild("", false); } if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild)) diff --git a/indra/llui/llundo.cpp b/indra/llui/llundo.cpp index 7c4c183a30..7a867357f8 100644 --- a/indra/llui/llundo.cpp +++ b/indra/llui/llundo.cpp @@ -72,7 +72,7 @@ LLUndoBuffer::~LLUndoBuffer() //----------------------------------------------------------------------------- // getNextAction() //----------------------------------------------------------------------------- -LLUndoBuffer::LLUndoAction* LLUndoBuffer::getNextAction(BOOL setClusterBegin) +LLUndoBuffer::LLUndoAction* LLUndoBuffer::getNextAction(bool setClusterBegin) { LLUndoAction *nextAction = mActions[mNextAction]; @@ -97,11 +97,11 @@ LLUndoBuffer::LLUndoAction* LLUndoBuffer::getNextAction(BOOL setClusterBegin) //----------------------------------------------------------------------------- // undoAction() //----------------------------------------------------------------------------- -BOOL LLUndoBuffer::undoAction() +bool LLUndoBuffer::undoAction() { if (!canUndo()) { - return FALSE; + return false; } S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions; @@ -118,7 +118,7 @@ BOOL LLUndoBuffer::undoAction() if (mNextAction == mFirstAction) { mOperationID--; - return FALSE; + return false; } // do wrap-around of index, but avoid negative numbers for modulo operator @@ -127,17 +127,17 @@ BOOL LLUndoBuffer::undoAction() mOperationID--; - return TRUE; + return true; } //----------------------------------------------------------------------------- // redoAction() //----------------------------------------------------------------------------- -BOOL LLUndoBuffer::redoAction() +bool LLUndoBuffer::redoAction() { if (!canRedo()) { - return FALSE; + return false; } mOperationID++; @@ -146,7 +146,7 @@ BOOL LLUndoBuffer::redoAction() { if (mNextAction == mLastAction) { - return FALSE; + return false; } mActions[mNextAction]->redo(); @@ -155,7 +155,7 @@ BOOL LLUndoBuffer::redoAction() mNextAction = (mNextAction + 1) % mNumActions; } - return TRUE; + return true; } //----------------------------------------------------------------------------- diff --git a/indra/llui/llundo.h b/indra/llui/llundo.h index a6da550126..f7ca6f66d2 100644 --- a/indra/llui/llundo.h +++ b/indra/llui/llundo.h @@ -48,11 +48,11 @@ public: LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count ); virtual ~LLUndoBuffer(); - LLUndoAction *getNextAction(BOOL setClusterBegin = TRUE); - BOOL undoAction(); - BOOL redoAction(); - BOOL canUndo() { return (mNextAction != mFirstAction); } - BOOL canRedo() { return (mNextAction != mLastAction); } + LLUndoAction *getNextAction(bool setClusterBegin = true); + bool undoAction(); + bool redoAction(); + bool canUndo() { return (mNextAction != mFirstAction); } + bool canRedo() { return (mNextAction != mLastAction); } void flushActions(); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 77e9edf5e5..08f5d011f1 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -395,7 +395,7 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0)) { - return TRUE; + return true; } } else if (path_parts == (actual_parts-1)) @@ -407,7 +407,7 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const ; if((x>= 0 && x<= 256) && (y>= 0 && y<= 256)) { - return TRUE; + return true; } } else if (path_parts == (actual_parts-2)) @@ -416,11 +416,11 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const LLStringUtil::convertToS32(path_array[path_parts-1],x); if(x>= 0 && x<= 256) { - return TRUE; + return true; } } - return FALSE; + return false; } // diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 5d0f5479f6..7d3728d790 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -107,7 +107,7 @@ public: bool isWikiLinkCorrect(const std::string &url) const; - virtual bool isSLURLvalid(const std::string &url) const { return TRUE; }; + virtual bool isSLURLvalid(const std::string &url) const { return true; }; protected: std::string getIDStringFromUrl(const std::string &url) const; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 4a6d10a790..894c8f6a32 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -66,11 +66,11 @@ bool LLView::sDebugRectsShowNames = true; bool LLView::sDebugKeys = false; bool LLView::sDebugMouseHandling = false; std::string LLView::sMouseHandlerMessage; -BOOL LLView::sForceReshape = FALSE; +bool LLView::sForceReshape = false; std::set LLView::sPreviewHighlightedElements; -BOOL LLView::sHighlightingDiffs = FALSE; +bool LLView::sHighlightingDiffs = false; LLView* LLView::sPreviewClickedElement = NULL; -BOOL LLView::sDrawPreviewHighlights = FALSE; +bool LLView::sDrawPreviewHighlights = false; S32 LLView::sLastLeftXML = S32_MIN; S32 LLView::sLastBottomXML = S32_MIN; std::vector LLViewDrawContext::sDrawContextStack; @@ -79,12 +79,12 @@ LLView::DrilldownFunc LLView::sDrilldown = boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT); //#if LL_DEBUG -BOOL LLView::sIsDrawing = FALSE; +bool LLView::sIsDrawing = false; //#endif // Compiler optimization, generate extern template template class LLView* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; static LLDefaultChildRegistry::Register r("view"); @@ -147,7 +147,7 @@ LLView::LLView(const LLView::Params& p) mReshapeFlags(FOLLOWS_NONE), mFromXUI(p.from_xui), mIsFocusRoot(p.focus_root), - mLastVisible(FALSE), + mLastVisible(false), mHoverCursor(getCursorFromString(p.hover_cursor)), mEnabled(p.enabled), mMouseOpaque(p.mouse_opaque), @@ -171,7 +171,7 @@ LLView::~LLView() { LL_DEBUGS() << "Deleting view " << mName << " during UI draw() phase" << LL_ENDL; } -// llassert(LLView::sIsDrawing == FALSE); +// llassert(LLView::sIsDrawing == false); // llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators @@ -196,15 +196,15 @@ LLView::~LLView() } // virtual -BOOL LLView::isCtrl() const +bool LLView::isCtrl() const { - return FALSE; + return false; } // virtual -BOOL LLView::isPanel() const +bool LLView::isPanel() const { - return FALSE; + return false; } void LLView::setToolTip(const LLStringExplicit& msg) @@ -212,10 +212,10 @@ void LLView::setToolTip(const LLStringExplicit& msg) mToolTipMsg = msg; } -BOOL LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text) +bool LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text) { mToolTipMsg.setArg(key, text); - return TRUE; + return true; } void LLView::setToolTipArgs( const LLStringUtil::format_map_t& args ) @@ -230,7 +230,7 @@ void LLView::setRect(const LLRect& rect) updateBoundingRect(); } -void LLView::setUseBoundingRect( BOOL use_bounding_rect ) +void LLView::setUseBoundingRect( bool use_bounding_rect ) { if (mUseBoundingRect != use_bounding_rect) { @@ -239,7 +239,7 @@ void LLView::setUseBoundingRect( BOOL use_bounding_rect ) } } -BOOL LLView::getUseBoundingRect() const +bool LLView::getUseBoundingRect() const { return mUseBoundingRect; } @@ -357,16 +357,16 @@ void LLView::removeChild(LLView* child) updateBoundingRect(); } -BOOL LLView::isInVisibleChain() const +bool LLView::isInVisibleChain() const { - BOOL visible = TRUE; + bool visible = true; const LLView* viewp = this; while(viewp) { if (!viewp->getVisible()) { - visible = FALSE; + visible = false; break; } viewp = viewp->getParent(); @@ -375,16 +375,16 @@ BOOL LLView::isInVisibleChain() const return visible; } -BOOL LLView::isInEnabledChain() const +bool LLView::isInEnabledChain() const { - BOOL enabled = TRUE; + bool enabled = true; const LLView* viewp = this; while(viewp) { if (!viewp->getEnabled()) { - enabled = FALSE; + enabled = false; break; } viewp = viewp->getParent(); @@ -444,13 +444,13 @@ std::string LLView::getPathname(const LLView* view) } // virtual -BOOL LLView::canFocusChildren() const +bool LLView::canFocusChildren() const { - return TRUE; + return true; } //virtual -void LLView::setEnabled(BOOL enabled) +void LLView::setEnabled(bool enabled) { mEnabled = enabled; } @@ -468,9 +468,9 @@ bool LLView::isAvailable(const LLView* view) } //virtual -BOOL LLView::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLView::setLabelArg( const std::string& key, const LLStringExplicit& text ) { - return FALSE; + return false; } //virtual @@ -485,20 +485,20 @@ LLRect LLView::getRequiredRect() return mRect; } -BOOL LLView::focusNextRoot() +bool LLView::focusNextRoot() { LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); return LLView::focusNext(result); } -BOOL LLView::focusPrevRoot() +bool LLView::focusPrevRoot() { LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); return LLView::focusPrev(result); } // static -BOOL LLView::focusNext(LLView::child_list_t & result) +bool LLView::focusNext(LLView::child_list_t & result) { LLView::child_list_reverse_iter_t focused = result.rend(); for(LLView::child_list_reverse_iter_t iter = result.rbegin(); @@ -523,18 +523,18 @@ BOOL LLView::focusNext(LLView::child_list_t & result) if((*next)->isCtrl()) { LLUICtrl * ctrl = static_cast(*next); - ctrl->setFocus(TRUE); + ctrl->setFocus(true); ctrl->onTabInto(); gFocusMgr.triggerFocusFlash(); - return TRUE; + return true; } ++next; } - return FALSE; + return false; } // static -BOOL LLView::focusPrev(LLView::child_list_t & result) +bool LLView::focusPrev(LLView::child_list_t & result) { LLView::child_list_iter_t focused = result.end(); for(LLView::child_list_iter_t iter = result.begin(); @@ -561,15 +561,15 @@ BOOL LLView::focusPrev(LLView::child_list_t & result) LLUICtrl * ctrl = static_cast(*next); if (!ctrl->hasFocus()) { - ctrl->setFocus(TRUE); + ctrl->setFocus(true); ctrl->onTabInto(); gFocusMgr.triggerFocusFlash(); } - return TRUE; + return true; } ++next; } - return FALSE; + return false; } // delete all children. Override this function if you need to @@ -590,7 +590,7 @@ void LLView::deleteAllChildren() updateBoundingRect(); } -void LLView::setAllChildrenEnabled(BOOL b) +void LLView::setAllChildrenEnabled(bool b) { BOOST_FOREACH(LLView* viewp, mChildList) { @@ -599,7 +599,7 @@ void LLView::setAllChildrenEnabled(BOOL b) } // virtual -void LLView::setVisible(BOOL visible) +void LLView::setVisible(bool visible) { if ( mVisible != visible ) { @@ -617,10 +617,10 @@ void LLView::setVisible(BOOL visible) } // virtual -void LLView::onVisibilityChange ( BOOL new_visibility ) +void LLView::onVisibilityChange ( bool new_visibility ) { - BOOL old_visibility; - BOOL log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus(); + bool old_visibility; + bool log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus(); BOOST_FOREACH(LLView* viewp, mChildList) { if (!viewp) @@ -673,7 +673,7 @@ void LLView::translate(S32 x, S32 y) } // virtual -BOOL LLView::canSnapTo(const LLView* other_view) +bool LLView::canSnapTo(const LLView* other_view) { return other_view != this && other_view->getVisible(); } @@ -727,7 +727,7 @@ LLView* LLView::childrenHandleCharEvent(const std::string& desc, const METHOD& m { BOOST_FOREACH(LLView* viewp, mChildList) { - if ((viewp->*method)(c, mask, TRUE)) + if ((viewp->*method)(c, mask, true)) { if (LLView::sDebugKeys) { @@ -796,7 +796,7 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask) } LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, + bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -929,9 +929,9 @@ bool LLView::handleToolTip(S32 x, S32 y, MASK mask) return handled; } -BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent) +bool LLView::handleKey(KEY key, MASK mask, bool called_from_parent) { - BOOL handled = FALSE; + bool handled = false; if (getVisible() && getEnabled()) { @@ -956,14 +956,14 @@ BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent) if( !handled && !called_from_parent && mParentView) { // Upward traversal - handled = mParentView->handleKey( key, mask, FALSE ); + handled = mParentView->handleKey( key, mask, false ); } return handled; } -BOOL LLView::handleKeyUp(KEY key, MASK mask, BOOL called_from_parent) +bool LLView::handleKeyUp(KEY key, MASK mask, bool called_from_parent) { - BOOL handled = FALSE; + bool handled = false; if (getVisible() && getEnabled()) { @@ -988,28 +988,28 @@ BOOL LLView::handleKeyUp(KEY key, MASK mask, BOOL called_from_parent) if (!handled && !called_from_parent && mParentView) { // Upward traversal - handled = mParentView->handleKeyUp(key, mask, FALSE); + handled = mParentView->handleKeyUp(key, mask, false); } return handled; } // Called from handleKey() // Handles key in this object. Checking parents and children happens in handleKey() -BOOL LLView::handleKeyHere(KEY key, MASK mask) +bool LLView::handleKeyHere(KEY key, MASK mask) { - return FALSE; + return false; } // Called from handleKey() // Handles key in this object. Checking parents and children happens in handleKey() -BOOL LLView::handleKeyUpHere(KEY key, MASK mask) +bool LLView::handleKeyUpHere(KEY key, MASK mask) { - return FALSE; + return false; } -BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +bool LLView::handleUnicodeChar(llwchar uni_char, bool called_from_parent) { - BOOL handled = FALSE; + bool handled = false; if (getVisible() && getEnabled()) { @@ -1032,7 +1032,7 @@ BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) if (!handled && !called_from_parent && mParentView) { // Upward traversal - handled = mParentView->handleUnicodeChar(uni_char, FALSE); + handled = mParentView->handleUnicodeChar(uni_char, false); } if (handled) @@ -1050,7 +1050,7 @@ bool LLView::handleUnicodeCharHere(llwchar uni_char ) } -BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +bool LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) @@ -1337,13 +1337,13 @@ void LLView::drawDebugRect() debug_rect.getWidth(), debug_rect.getHeight()); LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, NULL, FALSE); + S32_MAX, S32_MAX, NULL, false); } } LLUI::popMatrix(); } -void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_draw) +void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, bool force_draw) { if (childp && childp->getParent() == this) { @@ -1366,7 +1366,7 @@ void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_dr } -void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLView::reshape(S32 width, S32 height, bool called_from_parent) { // compute how much things changed and apply reshape logic to children S32 delta_width = width - getRect().getWidth(); @@ -1440,7 +1440,7 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) { if (mParentView) { - mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), FALSE); + mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), false); } } @@ -1555,11 +1555,11 @@ LLRect LLView::getLocalSnapRect() const return local_snap_rect; } -BOOL LLView::hasAncestor(const LLView* parentp) const +bool LLView::hasAncestor(const LLView* parentp) const { if (!parentp) { - return FALSE; + return false; } LLView* viewp = getParent(); @@ -1567,17 +1567,17 @@ BOOL LLView::hasAncestor(const LLView* parentp) const { if (viewp == parentp) { - return TRUE; + return true; } viewp = viewp->getParent(); } - return FALSE; + return false; } //----------------------------------------------------------------------------- -BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const +bool LLView::childHasKeyboardFocus( const std::string& childname ) const { LLView *focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); @@ -1585,18 +1585,18 @@ BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const { if (focus->getName() == childname) { - return TRUE; + return true; } focus = focus->getParent(); } - return FALSE; + return false; } //----------------------------------------------------------------------------- -BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const +bool LLView::hasChild(const std::string& childname, bool recurse) const { return findChildView(childname, recurse) != NULL; } @@ -1604,12 +1604,12 @@ BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const //----------------------------------------------------------------------------- // getChildView() //----------------------------------------------------------------------------- -LLView* LLView::getChildView(const std::string& name, BOOL recurse) const +LLView* LLView::getChildView(const std::string& name, bool recurse) const { return getChild(name, recurse); } -LLView* LLView::findChildView(const std::string& name, BOOL recurse) const +LLView* LLView::findChildView(const std::string& name, bool recurse) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -1638,21 +1638,21 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const return NULL; } -BOOL LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const +bool LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const { 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 +bool LLView::pointInView(S32 x, S32 y, EHitTestType type) const { return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT) ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) : mRect.localPointInRect( x, y ); } -BOOL LLView::blockMouseEvent(S32 x, S32 y) const +bool LLView::blockMouseEvent(S32 x, S32 y) const { return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT); } @@ -1778,21 +1778,21 @@ LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S3 // 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, S32 min_overlap_pixels) +bool LLView::translateIntoRect(const LLRect& constraint, S32 min_overlap_pixels) { LLCoordGL translation = getNeededTranslation(getRect(), constraint, min_overlap_pixels); if (translation.mX != 0 || translation.mY != 0) { translate(translation.mX, translation.mY); - return TRUE; + return true; } - return FALSE; + 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, S32 min_overlap_pixels) +bool LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels) { LLCoordGL translation = getNeededTranslation(getRect(), inside, min_overlap_pixels); @@ -1828,9 +1828,9 @@ BOOL LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& } } - return TRUE; + return true; } - return FALSE; + return false; } @@ -1842,7 +1842,7 @@ void LLView::centerWithin(const LLRect& bounds) translate( left - getRect().mLeft, bottom - getRect().mBottom ); } -BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const +bool LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const { const LLView* cur_view = this; const LLView* root_view = NULL; @@ -1853,7 +1853,7 @@ BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, co { *other_x = x; *other_y = y; - return TRUE; + return true; } x += cur_view->getRect().mLeft; @@ -1876,16 +1876,16 @@ BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, co { *other_x = x; *other_y = y; - return TRUE; + return true; } } *other_x = x; *other_y = y; - return FALSE; + return false; } -BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const +bool LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const { LLRect cur_rect = local; const LLView* cur_view = this; @@ -1896,7 +1896,7 @@ BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLV if (cur_view == other_view) { *other = cur_rect; - return TRUE; + return true; } cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom); @@ -1916,12 +1916,12 @@ BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLV if (cur_view == root_view) { *other = cur_rect; - return TRUE; + return true; } } *other = cur_rect; - return FALSE; + return false; } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 39eaf12913..8ac23f9f88 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -61,8 +61,8 @@ const U32 FOLLOWS_TOP = 0x10; const U32 FOLLOWS_BOTTOM = 0x20; const U32 FOLLOWS_ALL = 0x33; -const BOOL MOUSE_OPAQUE = TRUE; -const BOOL NOT_MOUSE_OPAQUE = FALSE; +const bool MOUSE_OPAQUE = true; +const bool NOT_MOUSE_OPAQUE = false; const U32 GL_NAME_UI_RESERVED = 2; @@ -169,7 +169,7 @@ private: LLView(const LLView& other); public: //#if LL_DEBUG - static BOOL sIsDrawing; + static bool sIsDrawing; //#endif enum ESoundFlags { @@ -210,19 +210,19 @@ public: virtual ~LLView(); // Some UI widgets need to be added as controls. Others need to - // be added as regular view children. isCtrl should return TRUE + // be added as regular view children. isCtrl should return true // if a widget needs to be added as a ctrl - virtual BOOL isCtrl() const; + virtual bool isCtrl() const; - virtual BOOL isPanel() const; + virtual bool isPanel() const; // // MANIPULATORS // - void setMouseOpaque( BOOL b ) { mMouseOpaque = b; } - BOOL getMouseOpaque() const { return mMouseOpaque; } + void setMouseOpaque( bool b ) { mMouseOpaque = b; } + bool getMouseOpaque() const { return mMouseOpaque; } void setToolTip( const LLStringExplicit& msg ); - BOOL setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text ); + bool setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text ); void setToolTipArgs( const LLStringUtil::format_map_t& args ); virtual void setRect(const LLRect &rect); @@ -238,8 +238,8 @@ public: void setSoundFlags(U8 flags) { mSoundFlags = flags; } void setName(std::string name) { mName = name; } - void setUseBoundingRect( BOOL use_bounding_rect ); - BOOL getUseBoundingRect() const; + void setUseBoundingRect( bool use_bounding_rect ); + bool getUseBoundingRect() const; ECursorType getHoverCursor() { return mHoverCursor; } @@ -257,7 +257,7 @@ public: // remove the specified child from the view, and set it's parent to NULL. virtual void removeChild(LLView* view); - virtual BOOL postBuild() { return TRUE; } + virtual bool postBuild() { return true; } const child_tab_order_t& getTabOrder() const { return mTabOrder; } @@ -265,15 +265,15 @@ public: S32 getDefaultTabGroup() const { return mDefaultTabGroup; } S32 getLastTabGroup() { return mLastTabGroup; } - BOOL isInVisibleChain() const; - BOOL isInEnabledChain() const; + bool isInVisibleChain() const; + bool isInEnabledChain() const; - void setFocusRoot(BOOL b) { mIsFocusRoot = b; } - BOOL isFocusRoot() const { return mIsFocusRoot; } - virtual BOOL canFocusChildren() const; + void setFocusRoot(bool b) { mIsFocusRoot = b; } + bool isFocusRoot() const { return mIsFocusRoot; } + virtual bool canFocusChildren() const; - BOOL focusNextRoot(); - BOOL focusPrevRoot(); + bool focusNextRoot(); + bool focusPrevRoot(); // Normally we want the app menus to get priority on accelerated keys // However, sometimes we want to give specific views a first chance @@ -285,13 +285,13 @@ public: // children, etc. virtual void deleteAllChildren(); - void setAllChildrenEnabled(BOOL b); + void setAllChildrenEnabled(bool b); - virtual void setVisible(BOOL visible); - void setVisibleDirect(BOOL visible) { mVisible = visible; } - const BOOL& getVisible() const { return mVisible; } - virtual void setEnabled(BOOL enabled); - BOOL getEnabled() const { return mEnabled; } + virtual void setVisible(bool visible); + void setVisibleDirect(bool visible) { mVisible = visible; } + const bool& getVisible() const { return mVisible; } + virtual void setEnabled(bool enabled); + bool getEnabled() const { return mEnabled; } /// 'available' in this context means 'visible and enabled': in other /// words, can a user actually interact with this? virtual bool isAvailable() const; @@ -299,21 +299,21 @@ public: static bool isAvailable(const LLView* view); U8 getSoundFlags() const { return mSoundFlags; } - virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - virtual void onVisibilityChange ( BOOL new_visibility ); + virtual void onVisibilityChange ( bool new_visibility ); virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); - void pushVisible(BOOL visible) { mLastVisible = mVisible; setVisible(visible); } + void pushVisible(bool visible) { mLastVisible = mVisible; setVisible(visible); } void popVisible() { setVisible(mLastVisible); } - BOOL getLastVisible() const { return mLastVisible; } + bool getLastVisible() const { return mLastVisible; } U32 getFollows() const { return mReshapeFlags; } - BOOL followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; } - BOOL followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; } - BOOL followsTop() const { return mReshapeFlags & FOLLOWS_TOP; } - BOOL followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; } - BOOL followsAll() const { return mReshapeFlags & FOLLOWS_ALL; } + bool followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; } + bool followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; } + bool followsTop() const { return mReshapeFlags & FOLLOWS_TOP; } + bool followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; } + bool followsAll() const { return mReshapeFlags & FOLLOWS_ALL; } const LLRect& getRect() const { return mRect; } const LLRect& getBoundingRect() const { return mBoundingRect; } @@ -338,9 +338,9 @@ public: 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; - BOOL hasChild(const std::string& childname, BOOL recurse = FALSE) const; - BOOL childHasKeyboardFocus( const std::string& childname ) const; + bool hasAncestor(const LLView* parentp) const; + bool hasChild(const std::string& childname, bool recurse = false) const; + bool childHasKeyboardFocus( const std::string& childname ) const; // these iterators are used for collapsing various tree traversals into for loops typedef LLTreeDFSIter tree_iterator_t; @@ -365,25 +365,25 @@ public: // // Default behavior is to use reshape flags to resize child views - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); 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, S32 min_overlap_pixels = S32_MAX); - BOOL translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels = S32_MAX); + bool translateIntoRect( const LLRect& constraint, S32 min_overlap_pixels = S32_MAX); + bool translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels = S32_MAX); void centerWithin(const LLRect& bounds); void setShape(const LLRect& new_rect, bool by_user = false); 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); - virtual BOOL canSnapTo(const LLView* other_view); + virtual bool canSnapTo(const LLView* other_view); virtual void setSnappedTo(const LLView* snap_view); // inherited from LLFocusableElement - /* virtual */ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); - /* virtual */ BOOL handleKeyUp(KEY key, MASK mask, BOOL called_from_parent); - /* virtual */ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + /* virtual */ bool handleKey(KEY key, MASK mask, bool called_from_parent); + /* virtual */ bool handleKeyUp(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, + virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -394,8 +394,8 @@ public: void parseFollowsFlags(const LLView::Params& params); // Some widgets, like close box buttons, don't need to be saved - BOOL getFromXUI() const { return mFromXUI; } - void setFromXUI(BOOL b) { mFromXUI = b; } + bool getFromXUI() const { return mFromXUI; } + void setFromXUI(bool b) { mFromXUI = b; } typedef enum e_hit_test_type { @@ -403,13 +403,13 @@ public: HIT_TEST_IGNORE_BOUNDING_RECT }EHitTestType; - BOOL parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; - BOOL pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; - BOOL blockMouseEvent(S32 x, S32 y) const; + bool parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + bool pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + bool blockMouseEvent(S32 x, S32 y) const; // See LLMouseHandler virtuals for screenPointToLocal and localPointToScreen - BOOL localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const; - BOOL localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const; + bool localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const; + bool localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const; void screenRectToLocal( const LLRect& screen, LLRect* local ) const; void localRectToScreen( const LLRect& local, LLRect* screen ) const; @@ -449,22 +449,22 @@ public: // static method handles NULL pointer too static std::string getPathname(const LLView*); - template T* findChild(const std::string& name, BOOL recurse = TRUE) const + template T* findChild(const std::string& name, bool recurse = true) const { LLView* child = findChildView(name, recurse); T* result = dynamic_cast(child); return result; } - template T* getChild(const std::string& name, BOOL recurse = TRUE) const; + template T* getChild(const std::string& name, bool recurse = true) const; - template T& getChildRef(const std::string& name, BOOL recurse = TRUE) const + template T& getChildRef(const std::string& name, bool recurse = true) const { return *getChild(name, recurse); } - virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const; - virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const; + virtual LLView* getChildView(const std::string& name, bool recurse = true) const; + virtual LLView* findChildView(const std::string& name, bool recurse = true) const; template T* getDefaultWidget(const std::string& name) const { @@ -492,9 +492,9 @@ public: //static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node); // focuses the item in the list after the currently-focused item, wrapping if necessary - static BOOL focusNext(LLView::child_list_t & result); + static bool focusNext(LLView::child_list_t & result); // focuses the item in the list before the currently-focused item, wrapping if necessary - static BOOL focusPrev(LLView::child_list_t & result); + static bool focusPrev(LLView::child_list_t & result); // returns query for iterating over controls in tab order static const LLViewQuery & getTabOrderQuery(); @@ -511,9 +511,9 @@ public: // to be top-left based. static void setupParamsForExport(Params& p, LLView* parent); - //virtual BOOL addChildFromParam(const LLInitParam::BaseBlock& params) { return TRUE; } - virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL handleKeyUpHere(KEY key, MASK mask); + //virtual bool addChildFromParam(const LLInitParam::BaseBlock& params) { return true; } + virtual bool handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyUpHere(KEY key, MASK mask); virtual bool handleUnicodeCharHere(llwchar uni_char); virtual void handleReshape(const LLRect& rect, bool by_user); @@ -536,7 +536,7 @@ public: protected: void drawDebugRect(); - void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE); + 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); @@ -546,7 +546,7 @@ protected: LLView* childrenHandleKeyUp(KEY key, MASK mask); LLView* childrenHandleUnicodeChar(llwchar uni_char); LLView* childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, + bool drop, EDragAndDropType type, void* data, EAcceptance* accept, @@ -578,7 +578,7 @@ private: // adapter to blur distinction between handleKey() and handleUnicodeChar() // for childrenHandleCharEvent() - BOOL handleUnicodeCharWithDummyMask(llwchar uni_char, MASK /* dummy */, BOOL from_parent) + bool handleUnicodeCharWithDummyMask(llwchar uni_char, MASK /* dummy */, bool from_parent) { return handleUnicodeChar(uni_char, from_parent); } @@ -587,7 +587,7 @@ private: child_list_t mChildList; // location in pixels, relative to surrounding structure, bottom,left=0,0 - BOOL mVisible; + bool mVisible; LLRect mRect; LLRect mBoundingRect; @@ -600,18 +600,18 @@ private: S32 mDefaultTabGroup; S32 mLastTabGroup; - BOOL mEnabled; // Enabled means "accepts input that has an effect on the state of the application." + 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. - BOOL mMouseOpaque; // Opaque views handle all mouse events that are over their rect. + bool mMouseOpaque; // Opaque views handle all mouse events that are over their rect. LLUIString mToolTipMsg; // isNull() is true if none. U8 mSoundFlags; - BOOL mFromXUI; + bool mFromXUI; - BOOL mIsFocusRoot; - BOOL mUseBoundingRect; // hit test against bounding rectangle that includes all child elements + bool mIsFocusRoot; + bool mUseBoundingRect; // hit test against bounding rectangle that includes all child elements - BOOL mLastVisible; + bool mLastVisible; bool mInDraw; @@ -670,12 +670,12 @@ public: static std::string sMouseHandlerMessage; static S32 sSelectID; static std::set sPreviewHighlightedElements; // DEV-16869 - static BOOL sHighlightingDiffs; // DEV-16869 + static bool sHighlightingDiffs; // DEV-16869 static LLView* sPreviewClickedElement; // DEV-16869 - static BOOL sDrawPreviewHighlights; + static bool sDrawPreviewHighlights; static S32 sLastLeftXML; static S32 sLastBottomXML; - static BOOL sForceReshape; + static bool sForceReshape; }; namespace LLInitParam @@ -687,7 +687,7 @@ struct TypeValues : public LLInitParam::TypeValuesHelper T* LLView::getChild(const std::string& name, BOOL recurse) const +template T* LLView::getChild(const std::string& name, bool recurse) const { LLView* child = findChildView(name, recurse); T* result = dynamic_cast(child); @@ -726,7 +726,7 @@ template T* LLView::getChild(const std::string& name, BOOL recurse) co // require explicit specialization. See llbutton.cpp for an example. #ifndef LLVIEW_CPP extern template class LLView* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; #endif #endif //LL_LLVIEW_H diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp index 919267dcc6..bca566b7df 100644 --- a/indra/llui/llviewborder.cpp +++ b/indra/llui/llviewborder.cpp @@ -63,7 +63,7 @@ LLViewBorder::Params::Params() LLViewBorder::LLViewBorder(const LLViewBorder::Params& p) : LLView(p), mTexture( NULL ), - mHasKeyboardFocus( FALSE ), + mHasKeyboardFocus( false ), mBorderWidth(p.border_thickness), mHighlightLight(p.highlight_light_color()), mHighlightDark(p.highlight_dark_color()), @@ -114,7 +114,7 @@ void LLViewBorder::draw() } else { - llassert( FALSE ); // not implemented + llassert( false ); // not implemented } } @@ -239,7 +239,7 @@ void LLViewBorder::drawTwoPixelLines() gl_line_2d(left+1, bottom+1, right-1, bottom+1); } -BOOL LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style) +bool LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style) { if (node->hasAttribute("bevel_style")) { @@ -263,8 +263,8 @@ BOOL LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel { bevel_style = LLViewBorder::BEVEL_BRIGHT; } - return TRUE; + return true; } - return FALSE; + return false; } diff --git a/indra/llui/llviewborder.h b/indra/llui/llviewborder.h index 413ce39744..e287f79848 100644 --- a/indra/llui/llviewborder.h +++ b/indra/llui/llviewborder.h @@ -66,12 +66,12 @@ protected: public: virtual void setValue(const LLSD& val) { setRect(LLRect(val)); } - virtual BOOL isCtrl() const { return FALSE; } + virtual bool isCtrl() const { return false; } // llview functionality virtual void draw(); - static BOOL getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style); + static bool getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style); void setBorderWidth(S32 width) { mBorderWidth = width; } S32 getBorderWidth() const { return mBorderWidth; } @@ -87,7 +87,7 @@ public: EStyle getStyle() const { return mStyle; } - void setKeyboardFocusHighlight( BOOL b ) { mHasKeyboardFocus = b; } + void setKeyboardFocusHighlight( bool b ) { mHasKeyboardFocus = b; } private: void drawOnePixelLines(); @@ -103,7 +103,7 @@ private: LLUIColor mBackgroundColor; S32 mBorderWidth; LLPointer mTexture; - BOOL mHasKeyboardFocus; + bool mHasKeyboardFocus; }; #endif // LL_LLVIEWBORDER_H diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp index cb000aef74..87f83a6493 100644 --- a/indra/llui/llviewereventrecorder.cpp +++ b/indra/llui/llviewereventrecorder.cpp @@ -124,7 +124,7 @@ void LLViewerEventRecorder::updateMouseEventInfo(S32 local_x, S32 local_y, S32 g LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo after updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << LL_ENDL; } -void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype) { +void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, bool visibility, std::string event_subtype) { LLSD event=LLSD::emptyMap(); diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h index 6170005b2b..e749e1ab57 100644 --- a/indra/llui/llviewereventrecorder.h +++ b/indra/llui/llviewereventrecorder.h @@ -55,7 +55,7 @@ public: void logKeyEvent(KEY key, MASK mask); void logKeyUnicodeEvent(llwchar uni_char); - void logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype); + void logVisibilityChange(std::string xui, std::string name, bool visibility, std::string event_subtype); void clear_xui(); std::string get_xui(); diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index 66262609ae..3ad5e71a2e 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -34,12 +34,12 @@ void LLQuerySorter::sort(LLView * parent, viewList_t &children) const {} filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(children.empty(), TRUE); + return filterResult_t(children.empty(), true); } filterResult_t LLRootsFilter::operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(TRUE, FALSE); + return filterResult_t(true, false); } filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const @@ -58,7 +58,7 @@ filterResult_t LLTabStopFilter::operator() (const LLView* const view, const view filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(view->isCtrl(),TRUE); + return filterResult_t(view->isCtrl(),true); } // @@ -79,7 +79,7 @@ viewList_t LLViewQuery::run(LLView* view) const } viewList_t filtered_children; - filterResult_t post(TRUE, TRUE); + filterResult_t post(true, true); if(pre.second) { // run filters on children @@ -123,7 +123,7 @@ void LLViewQuery::filterChildren(LLView* parent_view, viewList_t & filtered_chil filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const { - filterResult_t result = filterResult_t(TRUE, TRUE); + filterResult_t result = filterResult_t(true, true); for(filterList_const_iter_t iter = filters.begin(); iter != filters.end(); iter++) diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h index 21bb1be26f..780f74f03c 100644 --- a/indra/llui/llviewquery.h +++ b/indra/llui/llviewquery.h @@ -35,7 +35,7 @@ class LLView; typedef std::list viewList_t; -typedef std::pair filterResult_t; +typedef std::pair filterResult_t; // Abstract base class for all query filters. class LLQueryFilter @@ -93,7 +93,7 @@ class LLWidgetTypeFilter : public LLQueryFilter { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(dynamic_cast(view) != NULL, TRUE); + return filterResult_t(dynamic_cast(view) != NULL, true); } }; diff --git a/indra/llui/llvirtualtrackball.cpp b/indra/llui/llvirtualtrackball.cpp index 6e86c4671f..78b35621b3 100644 --- a/indra/llui/llvirtualtrackball.cpp +++ b/indra/llui/llvirtualtrackball.cpp @@ -165,9 +165,9 @@ LLVirtualTrackball::~LLVirtualTrackball() { } -BOOL LLVirtualTrackball::postBuild() +bool LLVirtualTrackball::postBuild() { - return TRUE; + return true; } @@ -233,7 +233,7 @@ void LLVirtualTrackball::draw() } // hide the direction labels when disabled - BOOL enabled = isInEnabledChain(); + bool enabled = isInEnabledChain(); mLabelN->setVisible(enabled); mLabelE->setVisible(enabled); mLabelS->setVisible(enabled); @@ -491,26 +491,26 @@ bool LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask) return LLView::handleRightMouseDown(x, y, mask); } -BOOL LLVirtualTrackball::handleKeyHere(KEY key, MASK mask) +bool LLVirtualTrackball::handleKeyHere(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; switch (key) { case KEY_DOWN: onRotateTopClick(); - handled = TRUE; + handled = true; break; case KEY_LEFT: onRotateRightClick(); - handled = TRUE; + handled = true; break; case KEY_UP: onRotateBottomClick(); - handled = TRUE; + handled = true; break; case KEY_RIGHT: onRotateLeftClick(); - handled = TRUE; + handled = true; break; default: break; diff --git a/indra/llui/llvirtualtrackball.h b/indra/llui/llvirtualtrackball.h index 574847de75..ebf7c8ba7b 100644 --- a/indra/llui/llvirtualtrackball.h +++ b/indra/llui/llvirtualtrackball.h @@ -79,13 +79,13 @@ public: virtual ~LLVirtualTrackball(); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); virtual bool handleHover(S32 x, S32 y, MASK mask); virtual bool handleMouseUp(S32 x, S32 y, MASK mask); virtual bool handleMouseDown(S32 x, S32 y, MASK mask); virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); virtual void draw(); diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index c122ab1c9b..a60ccb537e 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -365,7 +365,7 @@ void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, cons // duplicate element choices LLXMLNodeList children; - mElementNode->getChildren("xs:element", children, FALSE); + mElementNode->getChildren("xs:element", children, false); for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it) { LLXMLNodePtr child_copy = child_it->second->deepCopy(); @@ -1426,7 +1426,7 @@ bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBl mEmptyLeafNode.push_back(false); - if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) ) + if( !XML_ParseBuffer(mParser, bytes_read, true ) ) { LL_WARNS("ReadXUI") << "Error while parsing file " << filename << LL_ENDL; XML_ParserFree( mParser ); diff --git a/indra/llui/llxyvector.cpp b/indra/llui/llxyvector.cpp index 94dc72c86f..40d5d8c903 100644 --- a/indra/llui/llxyvector.cpp +++ b/indra/llui/llxyvector.cpp @@ -66,7 +66,7 @@ LLXYVector::Params::Params() ghost_color("ghost_color"), area_color("area_color", LLColor4::grey4), grid_color("grid_color", LLColor4::grey % 0.25f), - logarithmic("logarithmic", FALSE) + logarithmic("logarithmic", false) { } @@ -142,12 +142,12 @@ LLXYVector::~LLXYVector() { } -BOOL LLXYVector::postBuild() +bool LLXYVector::postBuild() { mLogScaleX = (2 * log(mMaxValueX)) / mTouchArea->getRect().getWidth(); mLogScaleY = (2 * log(mMaxValueY)) / mTouchArea->getRect().getHeight(); - return TRUE; + return true; } void drawArrow(S32 tailX, S32 tailY, S32 tipX, S32 tipY, LLColor4 color) diff --git a/indra/llui/llxyvector.h b/indra/llui/llxyvector.h index 1f6c73387e..4a20275267 100644 --- a/indra/llui/llxyvector.h +++ b/indra/llui/llxyvector.h @@ -59,14 +59,14 @@ public: Optional ghost_color; Optional area_color; Optional grid_color; - Optional logarithmic; + Optional logarithmic; Params(); }; virtual ~LLXYVector(); - /*virtual*/ BOOL postBuild(); + /*virtual*/ bool postBuild(); virtual bool handleHover(S32 x, S32 y, MASK mask); virtual bool handleMouseUp(S32 x, S32 y, MASK mask); @@ -113,7 +113,7 @@ private: LLUIColor mAreaColor; LLUIColor mGridColor; - BOOL mLogarithmic; + bool mLogarithmic; F32 mLogScaleX; F32 mLogScaleY; }; -- cgit v1.2.3 From 3ffe63b8a4e8a3ceda3f6d204e4b5bb0c80d0870 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 16:49:48 +0100 Subject: Convert remaining BOOLs in llxml and introduce std::string_view --- indra/llui/llnotifications.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 239e573f1d..5b95c00e24 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -219,7 +219,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica bool show_notification = true; if (p.ignore.control.isProvided()) { - mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control); + mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control()); mInvertSetting = p.ignore.invert_control; } else if (mIgnore > IGNORE_NO) -- cgit v1.2.3 From d31de6afb2ac9f659efc13c438df727372fcac08 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 24 Feb 2024 00:54:57 +0200 Subject: Issue#884 Crash on ~LLSearchEditor Crash seems to be specific to LLFilterEditor and only in a couple specific floaters. Based on older calltacks, commiting on exit was crashing. So I'm making sure that panels that potentially do not own the element in question clean the callback in case panels get deleted before the search editor. --- indra/llui/llsearcheditor.cpp | 7 +++++++ indra/llui/llsearcheditor.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index 78bd06b67e..13051998bd 100644 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -104,6 +104,13 @@ LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p) } } +LLSearchEditor::~LLSearchEditor() +{ + mKeystrokeCallback = NULL; + mTextChangedCallback = NULL; + setCommitOnFocusLost(false); +} + //virtual void LLSearchEditor::draw() { diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h index b332967f9b..3f8c6323b0 100644 --- a/indra/llui/llsearcheditor.h +++ b/indra/llui/llsearcheditor.h @@ -74,7 +74,7 @@ protected: friend class LLUICtrlFactory; public: - virtual ~LLSearchEditor() {} + virtual ~LLSearchEditor(); /*virtual*/ void draw(); -- cgit v1.2.3 From a865d423974ea06dffa47798c81e98e7570b02ec Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Tue, 5 Mar 2024 17:03:11 +0100 Subject: viewer#819 Avoid reading the same XML file multiple times --- indra/llui/llfloater.cpp | 8 ++++++-- indra/llui/llfloater.h | 2 +- indra/llui/llmenubutton.cpp | 2 +- indra/llui/llpanel.cpp | 4 ++-- indra/llui/llpanel.h | 3 ++- indra/llui/lluictrlfactory.cpp | 4 ++-- indra/llui/lluictrlfactory.h | 6 +++--- indra/llui/llxuiparser.cpp | 27 +++++---------------------- 8 files changed, 22 insertions(+), 34 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 3a6ee50a68..87ab9d0b19 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -3419,12 +3419,16 @@ bool LLFloater::isVisible(const LLFloater* floater) return floater && floater->getVisible(); } -bool LLFloater::buildFromFile(const std::string& filename) +bool LLFloater::buildFromFile(const std::string& filename, bool cacheable) { LL_PROFILE_ZONE_SCOPED; + + llassert_msg(!cacheable || !mSingleInstance || !mReuseInstance, + "No needs to cache XML for floater with mSingleInstance AND mReuseInstance flags set"); + LLXMLNodePtr root; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root, LLDir::CURRENT_SKIN, cacheable)) { LL_WARNS() << "Couldn't find (or parse) floater from: " << filename << LL_ENDL; return false; diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index bc315785d3..50edffbb8d 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -209,7 +209,7 @@ public: // Don't export top/left for rect, only height/width static void setupParamsForExport(Params& p, LLView* parent); - bool buildFromFile(const std::string &filename); + bool buildFromFile(const std::string &filename, bool cacheable = false); boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 5374b7ea73..85aa766918 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -95,7 +95,7 @@ void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition posit } llassert(LLMenuGL::sMenuContainer != NULL); - LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance(), true); if (!menu) { LL_WARNS() << "Error loading menu_button menu" << LL_ENDL; diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index ba6a31eb9e..bba10bd792 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -803,13 +803,13 @@ boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t:: //----------------------------------------------------------------------------- // buildPanel() //----------------------------------------------------------------------------- -bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) +bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params, bool cacheable) { LL_PROFILE_ZONE_SCOPED; bool didPost = false; LLXMLNodePtr root; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root, LLDir::CURRENT_SKIN, cacheable)) { LL_WARNS() << "Couldn't parse panel from: " << filename << LL_ENDL; return didPost; diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 33883bf6a4..dd73a41132 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -107,7 +107,8 @@ protected: public: typedef std::vector ctrl_list_t; - bool buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); + bool buildFromFile(const std::string &filename, const LLPanel::Params& default_params, bool cacheable = false); + bool buildFromFile(const std::string &filename, bool cacheable = false) { return buildFromFile(filename, getDefaultParams(), cacheable); } static LLPanel* createFactoryPanel(const std::string& name); diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index dca777cc1f..706140fd49 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -157,7 +157,7 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid // getLayeredXMLNode() //----------------------------------------------------------------------------- bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root, - LLDir::ESkinConstraint constraint) + LLDir::ESkinConstraint constraint, bool cacheable) { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; std::vector paths = @@ -169,7 +169,7 @@ bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNo paths.push_back(xui_filename); } - return LLXMLNode::getLayeredXMLNode(root, paths); + return LLXMLNode::getLayeredXMLNode(root, paths, cacheable); } diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index 6e585abfc0..2e1cc75508 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -148,7 +148,7 @@ public: LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node ); template - static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry) + static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry, bool cacheable = false) { T* widget = NULL; @@ -156,7 +156,7 @@ public: { LLXMLNodePtr root_node; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node, LLDir::CURRENT_SKIN, cacheable)) { LL_WARNS() << "Couldn't parse XUI from path: " << instance().getCurFileName() << ", from filename: " << filename << LL_ENDL; goto fail; @@ -192,7 +192,7 @@ fail: static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL); static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root, - LLDir::ESkinConstraint constraint=LLDir::CURRENT_SKIN); + LLDir::ESkinConstraint constraint = LLDir::CURRENT_SKIN, bool cacheable = false); private: //NOTE: both friend declarations are necessary to keep both gcc and msvc happy diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index a60ccb537e..84507a58b6 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -28,6 +28,7 @@ #include "llxuiparser.h" +#include "lldir.h" #include "llxmlnode.h" #include "llfasttimer.h" #ifdef LL_USESYSTEMLIBS @@ -44,6 +45,7 @@ #include "lluicolor.h" #include "v3math.h" + using namespace BOOST_SPIRIT_CLASSIC_NS; const S32 MAX_STRING_ATTRIBUTE_SIZE = 40; @@ -1397,36 +1399,17 @@ bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBl mCurReadDepth = 0; setParseSilently(silent); - ScopedFile file(filename, "rb"); - if( !file.isOpen() ) + std::string xml = gDirUtilp->getFileContents(filename); + if (xml.empty()) { LL_WARNS("ReadXUI") << "Unable to open file " << filename << LL_ENDL; XML_ParserFree( mParser ); return false; } - S32 bytes_read = 0; - - S32 buffer_size = file.getRemainingBytes(); - void* buffer = XML_GetBuffer(mParser, buffer_size); - if( !buffer ) - { - LL_WARNS("ReadXUI") << "Unable to allocate XML buffer while reading file " << filename << LL_ENDL; - XML_ParserFree( mParser ); - return false; - } - - bytes_read = (S32)fread(buffer, 1, buffer_size, file.mFile); - if( bytes_read <= 0 ) - { - LL_WARNS("ReadXUI") << "Error while reading file " << filename << LL_ENDL; - XML_ParserFree( mParser ); - return false; - } - mEmptyLeafNode.push_back(false); - if( !XML_ParseBuffer(mParser, bytes_read, true ) ) + if (!XML_Parse(mParser, xml.data(), (int)xml.size(), true)) { LL_WARNS("ReadXUI") << "Error while parsing file " << filename << LL_ENDL; XML_ParserFree( mParser ); -- cgit v1.2.3 From 73318bbc6a48be00aa4d3f73cdfdba6875616f6d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 20 Mar 2024 22:06:59 +0200 Subject: viewer#1018 Crash at insertStringNoUndo getEditableIndex retuns pos as is if only one segment is present --- indra/llui/lltextbase.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 61b67e346e..bb3ce49b8b 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -845,7 +845,14 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s S32 old_len = getLength(); // length() returns character length S32 insert_len = wstr.length(); - pos = getEditableIndex(pos, true); + pos = getEditableIndex(pos, true); + if (pos > old_len) + { + pos = old_len; + // Should not happen, + // if you encounter this, check where wrong position comes from + llassert(false); + } segment_set_t::iterator seg_iter = getEditableSegIterContaining(pos); -- cgit v1.2.3 From 7fc5f7e649c564fa8479a72a45459d0cc427d0f8 Mon Sep 17 00:00:00 2001 From: RunitaiLinden Date: Thu, 2 May 2024 10:57:39 -0500 Subject: #1354 Make coroutines use LLCoros::Mutex instead of LLMutex (#1356) * #1354 Make coroutines use LLCoros::Mutex instead of LLMutex * #1354 Fix some more unsafe coroutine executions. * #1354 Implement changes requested by Nat --- indra/llui/llconsole.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 8fc2978bdd..6a44108741 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -380,7 +380,7 @@ void LLConsole::updateClass() void LLConsole::update() { { - LLMutexLock lock(&mMutex); + LLCoros::LockType lock(mMutex); while (!mLines.empty()) { -- cgit v1.2.3 From f9473e8afcb624cc1b101195bf15943ec372b56f Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 6 May 2024 16:52:34 +0200 Subject: secondlife/viewer#1333 BOOL to bool conversion leftovers: ternaries --- indra/llui/llaccordionctrl.cpp | 12 ++++++------ indra/llui/llaccordionctrltab.cpp | 4 ++-- indra/llui/llfloaterreg.cpp | 10 +++++----- indra/llui/llfolderviewitem.cpp | 12 +++++++----- indra/llui/lllayoutstack.cpp | 8 +++----- indra/llui/lllineeditor.cpp | 12 ++++++------ indra/llui/llmenugl.cpp | 6 +++--- indra/llui/llradiogroup.cpp | 2 +- indra/llui/llscrolllistctrl.cpp | 4 ++-- indra/llui/lltexteditor.cpp | 2 +- indra/llui/lltoolbar.cpp | 18 +++++++++--------- indra/llui/llurlentry.cpp | 2 +- indra/llui/llview.cpp | 4 ++-- indra/llui/llxuiparser.cpp | 2 +- 14 files changed, 49 insertions(+), 49 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index d88e1a1f11..f075aa564e 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -265,7 +265,7 @@ void LLAccordionCtrl::showScrollbar(S32 width, S32 height) void LLAccordionCtrl::hideScrollbar(S32 width, S32 height) { - if (mScrollbar->getVisible() == false) + if (!mScrollbar->getVisible()) return; mScrollbar->setVisible(false); @@ -391,7 +391,7 @@ void LLAccordionCtrl::updateNoTabsHelpTextVisibility() } } - mNoVisibleTabsHelpText->setVisible(visible_exists ? false : true); + mNoVisibleTabsHelpText->setVisible(!visible_exists); } void LLAccordionCtrl::arrangeSingle() @@ -407,7 +407,7 @@ void LLAccordionCtrl::arrangeSingle() { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs + if (!accordion_tab->getVisible()) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) { @@ -421,7 +421,7 @@ void LLAccordionCtrl::arrangeSingle() { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs + if (!accordion_tab->getVisible()) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) { @@ -469,7 +469,7 @@ void LLAccordionCtrl::arrangeMultiple() { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs + if (!accordion_tab->getVisible()) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) @@ -624,7 +624,7 @@ bool LLAccordionCtrl::autoScroll(S32 x, S32 y) } } - return scrolling ? true : false; + return scrolling; } void LLAccordionCtrl::updateLayout(S32 width, S32 height) diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index c8f75a1af2..5945c31407 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -643,7 +643,7 @@ void LLAccordionCtrlTab::setHeaderVisible(bool value) if (mHeader) { - mHeader->setVisible(value ? true : false); + mHeader->setVisible(value); } reshape(getRect().getWidth(), getRect().getHeight(), false); @@ -992,7 +992,7 @@ void LLAccordionCtrlTab::hideScrollbar(const LLRect& child_rect) if (!mContainerPanel || !mScrollbar) return; - if (mScrollbar->getVisible() == false) + if (!mScrollbar->getVisible()) return; mScrollbar->setVisible(false); diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index d87e4ce70f..1c3efaa9cf 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -281,15 +281,15 @@ bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key) bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key) { LLFloater* instance = findInstance(name, key); - if (LLFloater::isShown(instance)) + if (instance && instance->isShown()) { instance->closeHostedFloater(); return false; } - else - { - return showInstance(name, key, true) ? true : false; - } + + instance = showInstance(name, key, true); + + return instance != nullptr; } //static diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index bcbc0f9970..311056f541 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -484,7 +484,7 @@ void LLFolderViewItem::deselectItem(void) void LLFolderViewItem::selectItem(void) { - if (mIsSelected == false) + if (!mIsSelected) { mIsSelected = true; getViewModelItem()->selectItem(); @@ -1582,7 +1582,8 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) { - if (getRoot()->getAllowMultiSelect() == false) return; + if (!getRoot()->getAllowMultiSelect()) + return; LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem(); if (cur_selected_item == NULL) @@ -1593,14 +1594,15 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) bool reverse = false; LLFolderViewFolder* common_ancestor = getCommonAncestor(cur_selected_item, new_selection, reverse); - if (!common_ancestor) return; + 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) + while (cur_folder != common_ancestor) { cur_folder->gatherChildRangeExclusive(last_selected_item_from_cur, NULL, reverse, items_to_select_forward); @@ -1612,7 +1614,7 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) LLFolderViewItem* last_selected_item_from_new = new_selection; cur_folder = new_selection->getParentFolder(); - while(cur_folder != common_ancestor) + while (cur_folder != common_ancestor) { cur_folder->gatherChildRangeExclusive(last_selected_item_from_new, NULL, !reverse, items_to_select_reverse); diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 7c834e0127..cf8cfa6ea6 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -136,7 +136,7 @@ S32 LLLayoutPanel::getVisibleDim() const * (min_dim + (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt)))); } - + void LLLayoutPanel::setOrientation( LLView::EOrientation orientation ) { mOrientation = orientation; @@ -144,9 +144,7 @@ void LLLayoutPanel::setOrientation( LLView::EOrientation orientation ) ? getRect().getWidth() : getRect().getHeight())); - if (mAutoResize == false - && mUserResize == true - && mMinDim == -1 ) + if (!mAutoResize && mUserResize && mMinDim == -1) { setMinDim(layout_dim); } @@ -170,7 +168,7 @@ void LLLayoutPanel::reshape( S32 width, S32 height, bool called_from_parent /*= { if (width == getRect().getWidth() && height == getRect().getHeight() && !LLView::sForceReshape) return; - if (!mIgnoreReshape && mAutoResize == false) + if (!mIgnoreReshape && !mAutoResize) { mTargetDim = (mOrientation == LLLayoutStack::HORIZONTAL) ? width : height; LLLayoutStack* stackp = dynamic_cast(getParent()); diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 505216d0ff..8a04342af0 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1473,11 +1473,11 @@ bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) // handle ctrl-uparrow if we have a history enabled line editor. case KEY_UP: - if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) ) + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) { - if( mCurrentHistoryLine > mLineHistory.begin() ) + if (mCurrentHistoryLine > mLineHistory.begin()) { - mText.assign( *(--mCurrentHistoryLine) ); + mText.assign(*(--mCurrentHistoryLine)); setCursorToEnd(); } else @@ -1490,9 +1490,9 @@ bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) // handle [ctrl]-downarrow if we have a history enabled line editor case KEY_DOWN: - if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) ) + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) { - if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1 ) + if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1) { mText.assign( *(++mCurrentHistoryLine) ); setCursorToEnd(); @@ -2684,7 +2684,7 @@ void LLLineEditor::showContextMenu(S32 x, S32 y) // If the cursor is on a misspelled word, retrieve suggestions for it std::string misspelled_word = getMisspelledWord(mCursorPos); - if ((is_misspelled = !misspelled_word.empty()) == true) + if ((is_misspelled = !misspelled_word.empty())) { LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); } diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 25f32d9671..5dc92e555a 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -1188,16 +1188,16 @@ void LLMenuItemBranchGL::updateBranchParent(LLView* parentp) } } -void LLMenuItemBranchGL::onVisibilityChange( bool new_visibility ) +void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility) { - if (new_visibility == false && getBranch() && !getBranch()->getTornOff()) + if (!new_visibility && getBranch() && !getBranch()->getTornOff()) { getBranch()->setVisible(false); } LLMenuItemGL::onVisibilityChange(new_visibility); } -bool LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) +bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask) { LLMenuGL* branch = getBranch(); if (!branch) diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index 04553e1f9f..2a45a8118e 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -138,7 +138,7 @@ void LLRadioGroup::setIndexEnabled(S32 index, bool enabled) if (count == index) { child->setEnabled(enabled); - if (index == mSelectedIndex && enabled == false) + if (index == mSelectedIndex && !enabled) { setSelectedIndex(-1); } diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index dbfacafd00..535a880120 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1527,12 +1527,12 @@ bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected) { LLSD::Binary data1 = value.asBinary(); LLSD::Binary data2 = item->getValue().asBinary(); - found = std::equal(data1.begin(), data1.end(), data2.begin()) ? true : false; + found = std::equal(data1.begin(), data1.end(), data2.begin()); } } else { - found = item->getValue().asString() == value.asString() ? true : false; + found = item->getValue().asString() == value.asString(); } if (found) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 81eaec620c..438b3d96b1 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -2202,7 +2202,7 @@ void LLTextEditor::showContextMenu(S32 x, S32 y) // If the cursor is on a misspelled word, retrieve suggestions for it std::string misspelled_word = getMisspelledWord(mCursorPos); - if ((is_misspelled = !misspelled_word.empty()) == true) + if ((is_misspelled = !misspelled_word.empty())) { LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); } diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp index 4e961a0a18..1c1414dd74 100644 --- a/indra/llui/lltoolbar.cpp +++ b/indra/llui/lltoolbar.cpp @@ -1049,20 +1049,20 @@ bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, std::string& tooltip_msg) { // If we have a drop callback, that means that we can handle the drop - bool handled = (mHandleDropCallback ? true : false); - + bool handled = mHandleDropCallback != nullptr; + // if drop is set, it's time to call the callback to get the operation done if (handled && drop) { - handled = mHandleDropCallback(cargo_data, x, y ,this); + handled = mHandleDropCallback(cargo_data, x, y, this); } - + // We accept only single tool drop on toolbars - *accept = (handled ? ACCEPT_YES_SINGLE : ACCEPT_NO); - + *accept = handled ? ACCEPT_YES_SINGLE : ACCEPT_NO; + // We'll use that flag to change the visual aspect of the toolbar target on draw() mDragAndDropTarget = false; - + // Convert drag position into insert position and rank if (!isReadOnly() && handled && !drop) { @@ -1073,7 +1073,7 @@ bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, int orig_rank = getRankFromPosition(dragged_command); mDragRank = getRankFromPosition(x, y); // Don't DaD if we're dragging a command on itself - mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank-1) == orig_rank)) ? false : true); + mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank - 1) == orig_rank))); //LL_INFOS() << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << LL_ENDL; /* Do the following if you want to animate the button itself LLCommandId dragged_command(inv_item->getUUID()); @@ -1086,7 +1086,7 @@ bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, handled = false; } } - + return handled; } diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index aa9272f782..45668e4a87 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -229,7 +229,7 @@ bool LLUrlEntryBase::isWikiLinkCorrect(const std::string &labeled_url) const label = "http://" + label; } - return (LLUrlRegistry::instance().hasUrl(label)) ? false : true; + return !LLUrlRegistry::instance().hasUrl(label); } std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 57c635b019..30cc5cd10a 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -342,11 +342,11 @@ void LLView::removeChild(LLView* child) if (child->mParentView == this) { // if we are removing an item we are currently iterating over, that would be bad - llassert(child->mInDraw == false); + llassert(!child->mInDraw); mChildList.remove( child ); child->mParentView = NULL; child_tab_order_t::iterator found = mTabOrder.find(child); - if(found != mTabOrder.end()) + if (found != mTabOrder.end()) { mTabOrder.erase(found); } diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index 84507a58b6..6209058875 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -935,7 +935,7 @@ bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr) bool value; LLXUIParser& self = static_cast(parser); bool success = self.mCurReadNode->getBoolValue(1, &value); - *((bool*)val_ptr) = (value != false); + *((bool*)val_ptr) = value; return success; } -- cgit v1.2.3 From 2008f87f10d51a2f9372aa4a4d72e86ac94e1e81 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 13 May 2024 18:26:53 +0300 Subject: Revert "viewer#819 Avoid reading the same XML file multiple times" This reverts commit a865d423974ea06dffa47798c81e98e7570b02ec. Reason for revert: viewer#1420, reverting to not hold maint-A (is deepCopy not full?) --- indra/llui/llfloater.cpp | 8 ++------ indra/llui/llfloater.h | 2 +- indra/llui/llmenubutton.cpp | 2 +- indra/llui/llpanel.cpp | 4 ++-- indra/llui/llpanel.h | 3 +-- indra/llui/lluictrlfactory.cpp | 4 ++-- indra/llui/lluictrlfactory.h | 6 +++--- indra/llui/llxuiparser.cpp | 27 ++++++++++++++++++++++----- 8 files changed, 34 insertions(+), 22 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 25006abf2c..48693c6267 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -3522,16 +3522,12 @@ bool LLFloater::isVisible(const LLFloater* floater) return floater && floater->getVisible(); } -bool LLFloater::buildFromFile(const std::string& filename, bool cacheable) +bool LLFloater::buildFromFile(const std::string& filename) { LL_PROFILE_ZONE_SCOPED; - - llassert_msg(!cacheable || !mSingleInstance || !mReuseInstance, - "No needs to cache XML for floater with mSingleInstance AND mReuseInstance flags set"); - LLXMLNodePtr root; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root, LLDir::CURRENT_SKIN, cacheable)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { LL_WARNS() << "Couldn't find (or parse) floater from: " << filename << LL_ENDL; return false; diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 13d9bf5adc..f7e121ed7e 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -208,7 +208,7 @@ public: // Don't export top/left for rect, only height/width static void setupParamsForExport(Params& p, LLView* parent); - bool buildFromFile(const std::string &filename, bool cacheable = false); + bool buildFromFile(const std::string &filename); boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 85aa766918..5374b7ea73 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -95,7 +95,7 @@ void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition posit } llassert(LLMenuGL::sMenuContainer != NULL); - LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance(), true); + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); if (!menu) { LL_WARNS() << "Error loading menu_button menu" << LL_ENDL; diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index bba10bd792..ba6a31eb9e 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -803,13 +803,13 @@ boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t:: //----------------------------------------------------------------------------- // buildPanel() //----------------------------------------------------------------------------- -bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params, bool cacheable) +bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) { LL_PROFILE_ZONE_SCOPED; bool didPost = false; LLXMLNodePtr root; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root, LLDir::CURRENT_SKIN, cacheable)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { LL_WARNS() << "Couldn't parse panel from: " << filename << LL_ENDL; return didPost; diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index dd73a41132..33883bf6a4 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -107,8 +107,7 @@ protected: public: typedef std::vector ctrl_list_t; - bool buildFromFile(const std::string &filename, const LLPanel::Params& default_params, bool cacheable = false); - bool buildFromFile(const std::string &filename, bool cacheable = false) { return buildFromFile(filename, getDefaultParams(), cacheable); } + bool buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); static LLPanel* createFactoryPanel(const std::string& name); diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 706140fd49..dca777cc1f 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -157,7 +157,7 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid // getLayeredXMLNode() //----------------------------------------------------------------------------- bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root, - LLDir::ESkinConstraint constraint, bool cacheable) + LLDir::ESkinConstraint constraint) { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; std::vector paths = @@ -169,7 +169,7 @@ bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNo paths.push_back(xui_filename); } - return LLXMLNode::getLayeredXMLNode(root, paths, cacheable); + return LLXMLNode::getLayeredXMLNode(root, paths); } diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index 2e1cc75508..6e585abfc0 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -148,7 +148,7 @@ public: LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node ); template - static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry, bool cacheable = false) + static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry) { T* widget = NULL; @@ -156,7 +156,7 @@ public: { LLXMLNodePtr root_node; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node, LLDir::CURRENT_SKIN, cacheable)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) { LL_WARNS() << "Couldn't parse XUI from path: " << instance().getCurFileName() << ", from filename: " << filename << LL_ENDL; goto fail; @@ -192,7 +192,7 @@ fail: static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL); static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root, - LLDir::ESkinConstraint constraint = LLDir::CURRENT_SKIN, bool cacheable = false); + LLDir::ESkinConstraint constraint=LLDir::CURRENT_SKIN); private: //NOTE: both friend declarations are necessary to keep both gcc and msvc happy diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index 6209058875..f8d4a61721 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -28,7 +28,6 @@ #include "llxuiparser.h" -#include "lldir.h" #include "llxmlnode.h" #include "llfasttimer.h" #ifdef LL_USESYSTEMLIBS @@ -45,7 +44,6 @@ #include "lluicolor.h" #include "v3math.h" - using namespace BOOST_SPIRIT_CLASSIC_NS; const S32 MAX_STRING_ATTRIBUTE_SIZE = 40; @@ -1399,17 +1397,36 @@ bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBl mCurReadDepth = 0; setParseSilently(silent); - std::string xml = gDirUtilp->getFileContents(filename); - if (xml.empty()) + ScopedFile file(filename, "rb"); + if( !file.isOpen() ) { LL_WARNS("ReadXUI") << "Unable to open file " << filename << LL_ENDL; XML_ParserFree( mParser ); return false; } + S32 bytes_read = 0; + + S32 buffer_size = file.getRemainingBytes(); + void* buffer = XML_GetBuffer(mParser, buffer_size); + if( !buffer ) + { + LL_WARNS("ReadXUI") << "Unable to allocate XML buffer while reading file " << filename << LL_ENDL; + XML_ParserFree( mParser ); + return false; + } + + bytes_read = (S32)fread(buffer, 1, buffer_size, file.mFile); + if( bytes_read <= 0 ) + { + LL_WARNS("ReadXUI") << "Error while reading file " << filename << LL_ENDL; + XML_ParserFree( mParser ); + return false; + } + mEmptyLeafNode.push_back(false); - if (!XML_Parse(mParser, xml.data(), (int)xml.size(), true)) + if( !XML_ParseBuffer(mParser, bytes_read, true ) ) { LL_WARNS("ReadXUI") << "Error while parsing file " << filename << LL_ENDL; XML_ParserFree( mParser ); -- cgit v1.2.3 From 03c4458bdcc6821a3047f93b729d412e274ab9af Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Mon, 20 May 2024 13:22:55 -0500 Subject: #1392 GLTF Upload (#1394) * #1392 WIP -- Functional texture upload, stubbed out .bin upload. * #1392 GLTF Upload WIP -- Emulates successful upload Successfully uploads texture Emulates successful .gltf and .bin upload by injecting into local asset cache. Emulates rez from inventory by setting sculpt ID of selected object Currently fails in tinygltf parsing due to missing .bin * Add missing notification * Build fix * #1392 Add boost::json .gltf reading support. * #1392 boost::json GLTF writing prototype * Create gltf/README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * #1392 Add ability to render directly from LL::GLTF::Material * Fix for mac build * Mac build fix * #1392 AssetType and Inventory Type plumbing * #1392 More sane error handling and scheduling of uploads. * #1392 Actually attempt to upload glbin * Mac build fix, upload nudge * Mac build fix * Fix glTF asset uploads to server * Mac build fix (inline not static) * More consistent inline * Add glm, mac nudge. * #1392 For consistency with spec, start using glm over glh:: and LLFoo * Another attempt at placating Mac builds * Another Mac nudge * Mac build take 23 * #1392 Prune LLMatrix4a from GLTF namespace. * #1392 Fix for orientation being off (glm::quat is wxyz, not xyzw) * #1392 WIP -- Actually send the sculpt type and id, nudge readme and alpha rendering * #1392 Working download! * #1394 Add support for GLTFEnabled SimulatorFeature * #1392 Review feedback --------- Co-authored-by: Pepper Linden <3782201+rohvani@users.noreply.github.com> --- indra/llui/llui.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 86b23c8c93..23df0c11e8 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -77,7 +77,10 @@ enum EDragAndDropType DAD_PERSON = 17, DAD_SETTINGS = 18, DAD_MATERIAL = 19, - DAD_COUNT = 20, // number of types in this enum + DAD_GLTF = 20, + DAD_GLTF_BIN = 21, + + DAD_COUNT = 22, // number of types in this enum }; // Reasons for drags to be denied. -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/llui/llaccordionctrl.cpp | 1910 +++---- indra/llui/llaccordionctrl.h | 400 +- indra/llui/llaccordionctrltab.cpp | 2252 ++++---- indra/llui/llaccordionctrltab.h | 502 +- indra/llui/llbadge.cpp | 778 +-- indra/llui/llbadge.h | 354 +- indra/llui/llbutton.cpp | 2642 ++++----- indra/llui/llbutton.h | 812 +-- indra/llui/llchat.h | 224 +- indra/llui/llchatentry.cpp | 502 +- indra/llui/llchatentry.h | 212 +- indra/llui/llcheckboxctrl.cpp | 608 +-- indra/llui/llcheckboxctrl.h | 314 +- indra/llui/llcombobox.cpp | 2462 ++++----- indra/llui/llcombobox.h | 568 +- indra/llui/llconsole.cpp | 806 +-- indra/llui/llconsole.h | 312 +- indra/llui/llcontainerview.cpp | 600 +-- indra/llui/llcontainerview.h | 188 +- indra/llui/llctrlselectioninterface.cpp | 126 +- indra/llui/llctrlselectioninterface.h | 208 +- indra/llui/lldockablefloater.cpp | 526 +- indra/llui/lldockablefloater.h | 304 +- indra/llui/lldraghandle.cpp | 774 +-- indra/llui/lldraghandle.h | 278 +- indra/llui/lleditmenuhandler.h | 142 +- indra/llui/llfiltereditor.cpp | 92 +- indra/llui/llflashtimer.cpp | 196 +- indra/llui/llflashtimer.h | 148 +- indra/llui/llflatlistview.cpp | 2878 +++++----- indra/llui/llflatlistview.h | 1070 ++-- indra/llui/llfloater.cpp | 7470 +++++++++++++------------- indra/llui/llfloater.h | 1282 ++--- indra/llui/llfloaterreg.cpp | 1218 ++--- indra/llui/llfloaterreg.h | 316 +- indra/llui/llflyoutbutton.cpp | 154 +- indra/llui/llflyoutbutton.h | 136 +- indra/llui/llfocusmgr.cpp | 1018 ++-- indra/llui/llfocusmgr.h | 320 +- indra/llui/llfolderview.cpp | 4258 +++++++-------- indra/llui/llfolderview.h | 896 ++-- indra/llui/llfolderviewitem.cpp | 4736 ++++++++--------- indra/llui/llfolderviewitem.h | 996 ++-- indra/llui/llfolderviewmodel.h | 948 ++-- indra/llui/lliconctrl.cpp | 346 +- indra/llui/lliconctrl.h | 214 +- indra/llui/llkeywords.cpp | 1626 +++--- indra/llui/lllayoutstack.cpp | 2044 +++---- indra/llui/lllayoutstack.h | 432 +- indra/llui/lllineeditor.cpp | 5470 +++++++++---------- indra/llui/lllineeditor.h | 944 ++-- indra/llui/lllocalcliprect.cpp | 220 +- indra/llui/lllocalcliprect.h | 126 +- indra/llui/llmenubutton.cpp | 492 +- indra/llui/llmenubutton.h | 202 +- indra/llui/llmenugl.cpp | 8808 +++++++++++++++---------------- indra/llui/llmenugl.h | 1956 +++---- indra/llui/llmodaldialog.cpp | 696 +-- indra/llui/llmodaldialog.h | 166 +- indra/llui/llmultifloater.cpp | 1044 ++-- indra/llui/llmultifloater.h | 204 +- indra/llui/llmultislider.cpp | 1760 +++--- indra/llui/llmultislider.h | 322 +- indra/llui/llmultisliderctrl.cpp | 1050 ++-- indra/llui/llmultisliderctrl.h | 348 +- indra/llui/llnotifications.cpp | 4018 +++++++------- indra/llui/llnotifications.h | 2266 ++++---- indra/llui/llpanel.cpp | 1750 +++--- indra/llui/llpanel.h | 636 +-- indra/llui/llradiogroup.cpp | 1042 ++-- indra/llui/llradiogroup.h | 228 +- indra/llui/llresizebar.cpp | 752 +-- indra/llui/llresizebar.h | 176 +- indra/llui/llresizehandle.cpp | 770 +-- indra/llui/llresizehandle.h | 158 +- indra/llui/llresmgr.cpp | 534 +- indra/llui/llscrollbar.cpp | 1332 ++--- indra/llui/llscrollbar.h | 334 +- indra/llui/llscrollcontainer.cpp | 1604 +++--- indra/llui/llscrollcontainer.h | 314 +- indra/llui/llscrollingpanellist.cpp | 504 +- indra/llui/llscrollingpanellist.h | 204 +- indra/llui/llscrolllistcell.cpp | 1342 ++--- indra/llui/llscrolllistcell.h | 560 +- indra/llui/llscrolllistcolumn.cpp | 680 +-- indra/llui/llscrolllistcolumn.h | 344 +- indra/llui/llscrolllistctrl.cpp | 6896 ++++++++++++------------ indra/llui/llscrolllistctrl.h | 1128 ++-- indra/llui/llscrolllistitem.cpp | 408 +- indra/llui/llscrolllistitem.h | 284 +- indra/llui/llsearcheditor.cpp | 436 +- indra/llui/llsearcheditor.h | 228 +- indra/llui/llslider.cpp | 766 +-- indra/llui/llslider.h | 216 +- indra/llui/llsliderctrl.cpp | 978 ++-- indra/llui/llsliderctrl.h | 352 +- indra/llui/llspinctrl.cpp | 1008 ++-- indra/llui/llspinctrl.h | 248 +- indra/llui/llstatbar.cpp | 1432 ++--- indra/llui/llstatbar.h | 238 +- indra/llui/llstatgraph.cpp | 252 +- indra/llui/llstatgraph.h | 282 +- indra/llui/llstatview.cpp | 128 +- indra/llui/llstyle.cpp | 208 +- indra/llui/llstyle.h | 236 +- indra/llui/lltabcontainer.cpp | 4408 ++++++++-------- indra/llui/lltabcontainer.h | 660 +-- indra/llui/lltextbase.cpp | 7780 +++++++++++++-------------- indra/llui/lltextbase.h | 1516 +++--- indra/llui/lltextbox.cpp | 366 +- indra/llui/lltextbox.h | 174 +- indra/llui/lltexteditor.cpp | 6140 ++++++++++----------- indra/llui/lltexteditor.h | 702 +-- indra/llui/lltextparser.cpp | 478 +- indra/llui/lltimectrl.cpp | 908 ++-- indra/llui/lltimectrl.h | 258 +- indra/llui/lltoggleablemenu.cpp | 216 +- indra/llui/lltoggleablemenu.h | 142 +- indra/llui/lltoolbar.cpp | 2532 ++++----- indra/llui/lltoolbar.h | 662 +-- indra/llui/lltooltip.cpp | 1296 ++--- indra/llui/lltooltip.h | 370 +- indra/llui/lltransutil.cpp | 148 +- indra/llui/llui.cpp | 1476 +++--- indra/llui/lluictrl.cpp | 2274 ++++---- indra/llui/lluictrl.h | 682 +-- indra/llui/lluictrlfactory.cpp | 558 +- indra/llui/llundo.cpp | 348 +- indra/llui/llundo.h | 136 +- indra/llui/llurlentry.cpp | 3476 ++++++------ indra/llui/llurlentry.h | 1172 ++-- indra/llui/llview.cpp | 5764 ++++++++++---------- indra/llui/llview.h | 1474 +++--- indra/llui/llviewborder.cpp | 540 +- indra/llui/llviewborder.h | 220 +- indra/llui/llviewereventrecorder.cpp | 592 +-- indra/llui/llviewquery.cpp | 272 +- indra/llui/llviewquery.h | 274 +- indra/llui/llvirtualtrackball.cpp | 1040 ++-- indra/llui/llvirtualtrackball.h | 326 +- indra/llui/llxuiparser.cpp | 3530 ++++++------- indra/llui/llxyvector.h | 244 +- indra/llui/tests/llurlentry_test.cpp | 1870 +++---- 143 files changed, 82851 insertions(+), 82851 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index c40e78233d..26f5e4fbe2 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -1,955 +1,955 @@ -/** - * @file llaccordionctrl.cpp - * @brief Accordion panel implementation - * - * $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 "llaccordionctrl.h" -#include "llaccordionctrltab.h" - -#include "lluictrlfactory.h" // builds floaters from XML - -#include "llwindow.h" -#include "llfocusmgr.h" -#include "lllocalcliprect.h" - -#include "boost/bind.hpp" - -static const S32 BORDER_MARGIN = 2; -static const S32 PARENT_BORDER_MARGIN = 5; -static const S32 VERTICAL_MULTIPLE = 16; -static const F32 MIN_AUTO_SCROLL_RATE = 120.f; -static const F32 MAX_AUTO_SCROLL_RATE = 500.f; -static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; - -// LLAccordionCtrl =================================================================| - -static LLDefaultChildRegistry::Register t2("accordion"); - -LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) - , mFitParent(params.fit_parent) - , mAutoScrolling( false ) - , mAutoScrollRate( 0.f ) - , mSelectedTab( NULL ) - , mTabComparator( NULL ) - , mNoVisibleTabsHelpText(NULL) - , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString()) - , mSkipScrollToChild(false) -{ - initNoTabsWidget(params.no_matched_tabs_text); - - mSingleExpansion = params.single_expansion; - if (mFitParent && !mSingleExpansion) - { - LL_INFOS() << "fit_parent works best when combined with single_expansion" << LL_ENDL; - } -} - -LLAccordionCtrl::LLAccordionCtrl() : LLPanel() - , mAutoScrolling( false ) - , mAutoScrollRate( 0.f ) - , mSelectedTab( NULL ) - , mNoVisibleTabsHelpText(NULL) -{ - initNoTabsWidget(LLTextBox::Params()); - - mSingleExpansion = false; - mFitParent = false; - buildFromFile( "accordion_parent.xml"); -} - -//--------------------------------------------------------------------------------- -void LLAccordionCtrl::draw() -{ - if (mAutoScrolling) - { - // add acceleration to autoscroll - mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE); - } - else - { - // reset to minimum for next time - mAutoScrollRate = MIN_AUTO_SCROLL_RATE; - } - // clear this flag to be set on next call to autoScroll - mAutoScrolling = false; - - LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0); - - LLLocalClipRect clip(local_rect); - - LLPanel::draw(); -} - -//--------------------------------------------------------------------------------- -bool LLAccordionCtrl::postBuild() -{ - static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); - - LLRect scroll_rect; - scroll_rect.setOriginAndSize( - getRect().getWidth() - scrollbar_size, - 1, - scrollbar_size, - getRect().getHeight() - 1); - - LLScrollbar::Params sbparams; - sbparams.name("scrollable vertical"); - sbparams.rect(scroll_rect); - sbparams.orientation(LLScrollbar::VERTICAL); - sbparams.doc_size(mInnerRect.getHeight()); - sbparams.doc_pos(0); - sbparams.page_size(mInnerRect.getHeight()); - sbparams.step_size(VERTICAL_MULTIPLE); - sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); - sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2)); - - mScrollbar = LLUICtrlFactory::create(sbparams); - LLView::addChild(mScrollbar); - mScrollbar->setVisible(false); - mScrollbar->setFollowsRight(); - mScrollbar->setFollowsTop(); - mScrollbar->setFollowsBottom(); - - //if it was created from xml... - std::vector accordion_tabs; - for (child_list_const_iter_t it = getChildList()->begin(); - getChildList()->end() != it; ++it) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(*it); - if (accordion_tab == NULL) - continue; - if (std::find(mAccordionTabs.begin(), mAccordionTabs.end(), accordion_tab) == mAccordionTabs.end()) - { - accordion_tabs.push_back(accordion_tab); - } - } - - for (std::vector::reverse_iterator it = accordion_tabs.rbegin(); - it < accordion_tabs.rend(); ++it) - { - addCollapsibleCtrl(*it); - } - - arrange(); - - if (mSingleExpansion) - { - if (!mAccordionTabs[0]->getDisplayChildren()) - mAccordionTabs[0]->setDisplayChildren(true); - for (size_t i = 1; i < mAccordionTabs.size(); ++i) - { - if (mAccordionTabs[i]->getDisplayChildren()) - mAccordionTabs[i]->setDisplayChildren(false); - } - } - - updateNoTabsHelpTextVisibility(); - - return true; -} - - -//--------------------------------------------------------------------------------- -LLAccordionCtrl::~LLAccordionCtrl() -{ - mAccordionTabs.clear(); -} - -//--------------------------------------------------------------------------------- - -void LLAccordionCtrl::reshape(S32 width, S32 height, bool called_from_parent) -{ - // adjust our rectangle - LLRect rcLocal = getRect(); - rcLocal.mRight = rcLocal.mLeft + width; - rcLocal.mTop = rcLocal.mBottom + height; - - // get textbox a chance to reshape its content - mNoVisibleTabsHelpText->reshape(width, height, called_from_parent); - - setRect(rcLocal); - - // assume that help text is always fit accordion. - // necessary text paddings can be set via h_pad and v_pad - mNoVisibleTabsHelpText->setRect(getLocalRect()); - - arrange(); -} - -//--------------------------------------------------------------------------------- -bool LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - return LLPanel::handleRightMouseDown(x, y, mask); -} - -//--------------------------------------------------------------------------------- -void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta) -{ - for (size_t i = panel_num; i < mAccordionTabs.size(); ++i) - { - ctrlShiftVertical(mAccordionTabs[i],delta); - } -} - -//--------------------------------------------------------------------------------- -void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num) -{ - if (mSingleExpansion) - { - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - if (i == panel_num) - continue; - if (mAccordionTabs[i]->getDisplayChildren()) - mAccordionTabs[i]->setDisplayChildren(false); - } - - } - arrange(); -} - -void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height) -{ - calcRecuiredHeight(); - if (getRecuiredHeight() > height) - showScrollbar(width, height); - else - hideScrollbar(width, height); -} - -void LLAccordionCtrl::showScrollbar(S32 width, S32 height) -{ - bool was_visible = mScrollbar->getVisible(); - - mScrollbar->setVisible(true); - - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - ctrlSetLeftTopAndSize(mScrollbar - , width - scrollbar_size - PARENT_BORDER_MARGIN / 2 - , height - PARENT_BORDER_MARGIN - , scrollbar_size - , height - PARENT_BORDER_MARGIN * 2); - - mScrollbar->setPageSize(height); - mScrollbar->setDocParams(mInnerRect.getHeight(), mScrollbar->getDocPos()); - - if (was_visible) - { - S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1); - mScrollbar->setDocPos(scroll_pos); - } -} - -void LLAccordionCtrl::hideScrollbar(S32 width, S32 height) -{ - if (!mScrollbar->getVisible()) - return; - mScrollbar->setVisible(false); - - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - S32 panel_width = width - 2*BORDER_MARGIN; - - // Reshape all accordions and shift all draggers - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - LLRect panel_rect = mAccordionTabs[i]->getRect(); - ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_rect.mTop, panel_width, panel_rect.getHeight()); - } - - mScrollbar->setDocPos(0); - - if (!mAccordionTabs.empty()) - { - S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel - S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop; - shiftAccordionTabs(0, diff); - } -} - -//--------------------------------------------------------------------------------- -S32 LLAccordionCtrl::calcRecuiredHeight() -{ - S32 rec_height = 0; - - std::vector::iterator panel; - for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(*panel); - if(accordion_tab && accordion_tab->getVisible()) - { - rec_height += accordion_tab->getRect().getHeight(); - } - } - - mInnerRect.setLeftTopAndSize(0, rec_height + BORDER_MARGIN * 2, getRect().getWidth(), rec_height + BORDER_MARGIN); - - return mInnerRect.getHeight(); -} - -//--------------------------------------------------------------------------------- -void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height) -{ - if (!panel) - return; - LLRect panel_rect = panel->getRect(); - panel_rect.setLeftTopAndSize( left, top, width, height); - panel->reshape( width, height, 1); - panel->setRect(panel_rect); -} - -void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta) -{ - if (!panel) - return; - panel->translate(0,delta); -} - -//--------------------------------------------------------------------------------- - -void LLAccordionCtrl::addCollapsibleCtrl(LLView* view) -{ - LLAccordionCtrlTab* accordion_tab = dynamic_cast(view); - if (!accordion_tab) - return; - if (std::find(beginChild(), endChild(), accordion_tab) == endChild()) - addChild(accordion_tab); - mAccordionTabs.push_back(accordion_tab); - - accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, (S16)(mAccordionTabs.size() - 1)) ); - arrange(); -} - -void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view) -{ - LLAccordionCtrlTab* accordion_tab = dynamic_cast(view); - if(!accordion_tab) - return; - - if(std::find(beginChild(), endChild(), accordion_tab) != endChild()) - removeChild(accordion_tab); - - for (std::vector::iterator iter = mAccordionTabs.begin(); - iter != mAccordionTabs.end(); ++iter) - { - if (accordion_tab == (*iter)) - { - mAccordionTabs.erase(iter); - break; - } - } - - // if removed is selected - reset selection - if (mSelectedTab == view) - { - mSelectedTab = NULL; - } -} - -void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params) -{ - LLTextBox::Params tp = tb_params; - tp.rect(getLocalRect()); - mNoMatchedTabsOrigString = tp.initial_value().asString(); - mNoVisibleTabsHelpText = LLUICtrlFactory::create(tp, this); -} - -void LLAccordionCtrl::updateNoTabsHelpTextVisibility() -{ - bool visible_exists = false; - std::vector::const_iterator it = mAccordionTabs.begin(); - const std::vector::const_iterator it_end = mAccordionTabs.end(); - while (it < it_end) - { - if ((*(it++))->getVisible()) - { - visible_exists = true; - break; - } - } - - mNoVisibleTabsHelpText->setVisible(!visible_exists); -} - -void LLAccordionCtrl::arrangeSingle() -{ - S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter - S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel - S32 panel_width = getRect().getWidth() - 4; - S32 panel_height; - - S32 collapsed_height = 0; - - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - - if (!accordion_tab->getVisible()) // Skip hidden accordion tabs - continue; - if (!accordion_tab->isExpanded() ) - { - collapsed_height+=mAccordionTabs[i]->getRect().getHeight(); - } - } - - S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height; - - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - - if (!accordion_tab->getVisible()) // Skip hidden accordion tabs - continue; - if (!accordion_tab->isExpanded() ) - { - panel_height = accordion_tab->getRect().getHeight(); - } - else - { - if (mFitParent) - { - panel_height = expanded_height; - } - else - { - if (accordion_tab->getAccordionView()) - { - panel_height = accordion_tab->getAccordionView()->getRect().getHeight() + - accordion_tab->getHeaderHeight() + BORDER_MARGIN * 2; - } - else - { - panel_height = accordion_tab->getRect().getHeight(); - } - } - } - - // make sure at least header is shown - panel_height = llmax(panel_height, accordion_tab->getHeaderHeight()); - - ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); - panel_top -= mAccordionTabs[i]->getRect().getHeight(); - } - - show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); - updateLayout(getRect().getWidth(), getRect().getHeight()); -} - -void LLAccordionCtrl::arrangeMultiple() -{ - S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter - S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel - S32 panel_width = getRect().getWidth() - 4; - - //Calculate params - for (size_t i = 0; i < mAccordionTabs.size(); i++ ) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - - if (!accordion_tab->getVisible()) // Skip hidden accordion tabs - continue; - - if (!accordion_tab->isExpanded() ) - { - ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight()); - panel_top -= mAccordionTabs[i]->getRect().getHeight(); - } - else - { - S32 panel_height = accordion_tab->getRect().getHeight(); - - if (mFitParent) - { - // All expanded tabs will have equal height - panel_height = calcExpandedTabHeight(i, panel_top); - ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height); - - // Try to make accordion tab fit accordion view height. - // Accordion View should implement getRequiredRect() and provide valid height - S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight(); - optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN; - if (optimal_height < panel_height) - { - panel_height = optimal_height; - } - - // minimum tab height is equal to header height - if (mAccordionTabs[i]->getHeaderHeight() > panel_height) - { - panel_height = mAccordionTabs[i]->getHeaderHeight(); - } - } - - ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); - panel_top -= panel_height; - - } - } - - show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); - - updateLayout(getRect().getWidth(), getRect().getHeight()); -} - - -void LLAccordionCtrl::arrange() -{ - updateNoTabsHelpTextVisibility(); - - if (mAccordionTabs.empty()) - { - // Nothing to arrange - return; - } - - if (mAccordionTabs.size() == 1) - { - S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel - S32 panel_width = getRect().getWidth() - 4; - - LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[0]); - - LLRect panel_rect = accordion_tab->getRect(); - - S32 panel_height = getRect().getHeight() - BORDER_MARGIN * 2; - if (accordion_tab->getFitParent()) - panel_height = accordion_tab->getRect().getHeight(); - - ctrlSetLeftTopAndSize(accordion_tab, panel_rect.mLeft, panel_top, panel_width, panel_height); - - show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); - return; - } - - if (mSingleExpansion) - arrangeSingle(); - else - arrangeMultiple(); -} - -//--------------------------------------------------------------------------------- - -bool LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if (LLPanel::handleScrollWheel(x, y, clicks)) - return true; - if (mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) - return true; - return false; -} - -bool LLAccordionCtrl::handleKeyHere(KEY key, MASK mask) -{ - if (mScrollbar->getVisible() && mScrollbar->handleKeyHere(key, mask)) - return true; - return LLPanel::handleKeyHere(key, mask); -} - -bool LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, - bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - // Scroll folder view if needed. Never accepts a drag or drop. - *accept = ACCEPT_NO; - bool handled = autoScroll(x, y); - - if (!handled) - { - handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, - cargo_data, accept, tooltip_msg) != NULL; - } - return true; -} - -bool LLAccordionCtrl::autoScroll(S32 x, S32 y) -{ - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - bool scrolling = false; - if (mScrollbar->getVisible()) - { - LLRect rect_local(0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0); - LLRect screen_local_extents; - - // clip rect against root view - screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents); - rect_local.intersectWith(screen_local_extents); - - // autoscroll region should take up no more than one third of visible scroller area - S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10); - S32 auto_scroll_speed = ll_round(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); - - LLRect bottom_scroll_rect = screen_local_extents; - bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height; - if (bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax())) - { - mScrollbar->setDocPos(mScrollbar->getDocPos() + auto_scroll_speed); - mAutoScrolling = true; - scrolling = true; - } - - LLRect top_scroll_rect = screen_local_extents; - top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height; - if (top_scroll_rect.pointInRect(x, y) && (mScrollbar->getDocPos() > 0)) - { - mScrollbar->setDocPos(mScrollbar->getDocPos() - auto_scroll_speed); - mAutoScrolling = true; - scrolling = true; - } - } - - return scrolling; -} - -void LLAccordionCtrl::updateLayout(S32 width, S32 height) -{ - S32 panel_top = height - BORDER_MARGIN ; - if (mScrollbar->getVisible()) - panel_top += mScrollbar->getDocPos(); - - S32 panel_width = width - BORDER_MARGIN * 2; - - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - if (mScrollbar->getVisible()) - panel_width -= scrollbar_size; - - // set sizes for first panels and dragbars - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - if (!mAccordionTabs[i]->getVisible()) - continue; - LLRect panel_rect = mAccordionTabs[i]->getRect(); - ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_top, panel_width, panel_rect.getHeight()); - panel_top -= panel_rect.getHeight(); - } -} - -void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*) -{ - updateLayout(getRect().getWidth(), getRect().getHeight()); -} - -// virtual -void LLAccordionCtrl::onUpdateScrollToChild(const LLUICtrl *cntrl) -{ - if (mScrollbar && mScrollbar->getVisible() && !mSkipScrollToChild) - { - // same as scrollToShowRect - LLRect rect; - cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this); - - // Translate to parent coordinatess to check if we are in visible rectangle - rect.translate(getRect().mLeft, getRect().mBottom); - - if (!getRect().contains(rect)) - { - // for accordition's scroll, height is in pixels - // Back to local coords and calculate position for scroller - S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom; - S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop; - - S32 scroll_pos = llclamp(mScrollbar->getDocPos(), - bottom, // min vertical scroll - top); // max vertical scroll - - mScrollbar->setDocPos(scroll_pos); - } - } - - LLUICtrl::onUpdateScrollToChild(cntrl); -} - -void LLAccordionCtrl::onOpen(const LLSD& key) -{ - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - LLPanel* panel = dynamic_cast(accordion_tab->getAccordionView()); - if (panel != NULL) - { - panel->onOpen(key); - } - } -} - -S32 LLAccordionCtrl::notifyParent(const LLSD& info) -{ - if (info.has("action")) - { - std::string str_action = info["action"]; - if (str_action == "size_changes") - { - // - arrange(); - return 1; - } - if (str_action == "select_next") - { - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->hasFocus()) - { - while (++i < mAccordionTabs.size()) - { - if (mAccordionTabs[i]->getVisible()) - break; - } - if (i < mAccordionTabs.size()) - { - accordion_tab = dynamic_cast(mAccordionTabs[i]); - accordion_tab->notify(LLSD().with("action","select_first")); - return 1; - } - break; - } - } - return 0; - } - if (str_action == "select_prev") - { - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if (accordion_tab->hasFocus() && i > 0) - { - bool prev_visible_tab_found = false; - while (i > 0) - { - if (mAccordionTabs[--i]->getVisible()) - { - prev_visible_tab_found = true; - break; - } - } - - if (prev_visible_tab_found) - { - accordion_tab = dynamic_cast(mAccordionTabs[i]); - accordion_tab->notify(LLSD().with("action","select_last")); - return 1; - } - break; - } - } - return 0; - } - if (str_action == "select_current") - { - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - // Set selection to the currently focused tab. - if (mAccordionTabs[i]->hasFocus()) - { - if (mAccordionTabs[i] != mSelectedTab) - { - if (mSelectedTab) - { - mSelectedTab->setSelected(false); - } - mSelectedTab = mAccordionTabs[i]; - mSelectedTab->setSelected(true); - } - - return 1; - } - } - return 0; - } - if (str_action == "deselect_current") - { - // Reset selection to the currently selected tab. - if (mSelectedTab) - { - mSelectedTab->setSelected(false); - mSelectedTab = NULL; - return 1; - } - return 0; - } - } - else if (info.has("scrollToShowRect")) - { - LLRect screen_rc, local_rc; - screen_rc.setValue(info["scrollToShowRect"]); - screenRectToLocal(screen_rc, &local_rc); - - // Translate to parent coordinatess to check if we are in visible rectangle - local_rc.translate(getRect().mLeft, getRect().mBottom); - - if (!getRect().contains (local_rc)) - { - // Back to local coords and calculate position for scroller - S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom; - S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop; - - S32 scroll_pos = llclamp(mScrollbar->getDocPos(), - bottom, // min vertical scroll - top); // max vertical scroll - - mScrollbar->setDocPos(scroll_pos); - } - return 1; - } - else if (info.has("child_visibility_change")) - { - bool new_visibility = info["child_visibility_change"]; - if (new_visibility) - { - // there is at least one visible tab - mNoVisibleTabsHelpText->setVisible(false); - } - else - { - // it could be the latest visible tab, check all of them - updateNoTabsHelpTextVisibility(); - } - } - return LLPanel::notifyParent(info); -} - -void LLAccordionCtrl::reset() -{ - if (mScrollbar) - mScrollbar->setDocPos(0); -} - -void LLAccordionCtrl::expandDefaultTab() -{ - if (!mAccordionTabs.empty()) - { - LLAccordionCtrlTab* tab = mAccordionTabs.front(); - - if (!tab->getDisplayChildren()) - { - tab->setDisplayChildren(true); - } - - for (size_t i = 1; i < mAccordionTabs.size(); ++i) - { - tab = mAccordionTabs[i]; - - if (tab->getDisplayChildren()) - { - tab->setDisplayChildren(false); - } - } - - arrange(); - } -} - -void LLAccordionCtrl::sort() -{ - if (!mTabComparator) - { - LL_WARNS() << "No comparator specified for sorting accordion tabs." << LL_ENDL; - return; - } - - std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator)); - arrange(); -} - -void LLAccordionCtrl::setFilterSubString(const std::string& filter_string) -{ - LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(filter_string); - std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString; - LLStringUtil::format(text, args); - - mNoVisibleTabsHelpText->setValue(text); -} - -const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const -{ - typedef std::vector::const_iterator tabs_const_iterator; - - const LLAccordionCtrlTab* result = 0; - - for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i) - { - if ((*i)->isExpanded()) - { - result = *i; - break; - } - } - - return result; -} - -S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */) -{ - if (tab_index < 0) - { - return available_height; - } - - S32 collapsed_tabs_height = 0; - S32 num_expanded = 0; - - for (size_t n = tab_index; n < mAccordionTabs.size(); ++n) - { - if (!mAccordionTabs[n]->isExpanded()) - { - collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight(); - } - else - { - ++num_expanded; - } - } - - if (0 == num_expanded) - { - return available_height; - } - - S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN - expanded_tab_height /= num_expanded; - return expanded_tab_height; -} - -void LLAccordionCtrl::collapseAllTabs() -{ - if (mAccordionTabs.size() > 0) - { - for (size_t i = 0; i < mAccordionTabs.size(); ++i) - { - LLAccordionCtrlTab *tab = mAccordionTabs[i]; - - if (tab->getDisplayChildren()) - { - tab->setDisplayChildren(false); - } - } - arrange(); - } -} +/** + * @file llaccordionctrl.cpp + * @brief Accordion panel implementation + * + * $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 "llaccordionctrl.h" +#include "llaccordionctrltab.h" + +#include "lluictrlfactory.h" // builds floaters from XML + +#include "llwindow.h" +#include "llfocusmgr.h" +#include "lllocalcliprect.h" + +#include "boost/bind.hpp" + +static const S32 BORDER_MARGIN = 2; +static const S32 PARENT_BORDER_MARGIN = 5; +static const S32 VERTICAL_MULTIPLE = 16; +static const F32 MIN_AUTO_SCROLL_RATE = 120.f; +static const F32 MAX_AUTO_SCROLL_RATE = 500.f; +static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; + +// LLAccordionCtrl =================================================================| + +static LLDefaultChildRegistry::Register t2("accordion"); + +LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) + , mFitParent(params.fit_parent) + , mAutoScrolling( false ) + , mAutoScrollRate( 0.f ) + , mSelectedTab( NULL ) + , mTabComparator( NULL ) + , mNoVisibleTabsHelpText(NULL) + , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString()) + , mSkipScrollToChild(false) +{ + initNoTabsWidget(params.no_matched_tabs_text); + + mSingleExpansion = params.single_expansion; + if (mFitParent && !mSingleExpansion) + { + LL_INFOS() << "fit_parent works best when combined with single_expansion" << LL_ENDL; + } +} + +LLAccordionCtrl::LLAccordionCtrl() : LLPanel() + , mAutoScrolling( false ) + , mAutoScrollRate( 0.f ) + , mSelectedTab( NULL ) + , mNoVisibleTabsHelpText(NULL) +{ + initNoTabsWidget(LLTextBox::Params()); + + mSingleExpansion = false; + mFitParent = false; + buildFromFile( "accordion_parent.xml"); +} + +//--------------------------------------------------------------------------------- +void LLAccordionCtrl::draw() +{ + if (mAutoScrolling) + { + // add acceleration to autoscroll + mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE); + } + else + { + // reset to minimum for next time + mAutoScrollRate = MIN_AUTO_SCROLL_RATE; + } + // clear this flag to be set on next call to autoScroll + mAutoScrolling = false; + + LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + + LLLocalClipRect clip(local_rect); + + LLPanel::draw(); +} + +//--------------------------------------------------------------------------------- +bool LLAccordionCtrl::postBuild() +{ + static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); + + LLRect scroll_rect; + scroll_rect.setOriginAndSize( + getRect().getWidth() - scrollbar_size, + 1, + scrollbar_size, + getRect().getHeight() - 1); + + LLScrollbar::Params sbparams; + sbparams.name("scrollable vertical"); + sbparams.rect(scroll_rect); + sbparams.orientation(LLScrollbar::VERTICAL); + sbparams.doc_size(mInnerRect.getHeight()); + sbparams.doc_pos(0); + sbparams.page_size(mInnerRect.getHeight()); + sbparams.step_size(VERTICAL_MULTIPLE); + sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2)); + + mScrollbar = LLUICtrlFactory::create(sbparams); + LLView::addChild(mScrollbar); + mScrollbar->setVisible(false); + mScrollbar->setFollowsRight(); + mScrollbar->setFollowsTop(); + mScrollbar->setFollowsBottom(); + + //if it was created from xml... + std::vector accordion_tabs; + for (child_list_const_iter_t it = getChildList()->begin(); + getChildList()->end() != it; ++it) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(*it); + if (accordion_tab == NULL) + continue; + if (std::find(mAccordionTabs.begin(), mAccordionTabs.end(), accordion_tab) == mAccordionTabs.end()) + { + accordion_tabs.push_back(accordion_tab); + } + } + + for (std::vector::reverse_iterator it = accordion_tabs.rbegin(); + it < accordion_tabs.rend(); ++it) + { + addCollapsibleCtrl(*it); + } + + arrange(); + + if (mSingleExpansion) + { + if (!mAccordionTabs[0]->getDisplayChildren()) + mAccordionTabs[0]->setDisplayChildren(true); + for (size_t i = 1; i < mAccordionTabs.size(); ++i) + { + if (mAccordionTabs[i]->getDisplayChildren()) + mAccordionTabs[i]->setDisplayChildren(false); + } + } + + updateNoTabsHelpTextVisibility(); + + return true; +} + + +//--------------------------------------------------------------------------------- +LLAccordionCtrl::~LLAccordionCtrl() +{ + mAccordionTabs.clear(); +} + +//--------------------------------------------------------------------------------- + +void LLAccordionCtrl::reshape(S32 width, S32 height, bool called_from_parent) +{ + // adjust our rectangle + LLRect rcLocal = getRect(); + rcLocal.mRight = rcLocal.mLeft + width; + rcLocal.mTop = rcLocal.mBottom + height; + + // get textbox a chance to reshape its content + mNoVisibleTabsHelpText->reshape(width, height, called_from_parent); + + setRect(rcLocal); + + // assume that help text is always fit accordion. + // necessary text paddings can be set via h_pad and v_pad + mNoVisibleTabsHelpText->setRect(getLocalRect()); + + arrange(); +} + +//--------------------------------------------------------------------------------- +bool LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + return LLPanel::handleRightMouseDown(x, y, mask); +} + +//--------------------------------------------------------------------------------- +void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta) +{ + for (size_t i = panel_num; i < mAccordionTabs.size(); ++i) + { + ctrlShiftVertical(mAccordionTabs[i],delta); + } +} + +//--------------------------------------------------------------------------------- +void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num) +{ + if (mSingleExpansion) + { + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + if (i == panel_num) + continue; + if (mAccordionTabs[i]->getDisplayChildren()) + mAccordionTabs[i]->setDisplayChildren(false); + } + + } + arrange(); +} + +void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height) +{ + calcRecuiredHeight(); + if (getRecuiredHeight() > height) + showScrollbar(width, height); + else + hideScrollbar(width, height); +} + +void LLAccordionCtrl::showScrollbar(S32 width, S32 height) +{ + bool was_visible = mScrollbar->getVisible(); + + mScrollbar->setVisible(true); + + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + ctrlSetLeftTopAndSize(mScrollbar + , width - scrollbar_size - PARENT_BORDER_MARGIN / 2 + , height - PARENT_BORDER_MARGIN + , scrollbar_size + , height - PARENT_BORDER_MARGIN * 2); + + mScrollbar->setPageSize(height); + mScrollbar->setDocParams(mInnerRect.getHeight(), mScrollbar->getDocPos()); + + if (was_visible) + { + S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1); + mScrollbar->setDocPos(scroll_pos); + } +} + +void LLAccordionCtrl::hideScrollbar(S32 width, S32 height) +{ + if (!mScrollbar->getVisible()) + return; + mScrollbar->setVisible(false); + + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + S32 panel_width = width - 2*BORDER_MARGIN; + + // Reshape all accordions and shift all draggers + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + LLRect panel_rect = mAccordionTabs[i]->getRect(); + ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_rect.mTop, panel_width, panel_rect.getHeight()); + } + + mScrollbar->setDocPos(0); + + if (!mAccordionTabs.empty()) + { + S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel + S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop; + shiftAccordionTabs(0, diff); + } +} + +//--------------------------------------------------------------------------------- +S32 LLAccordionCtrl::calcRecuiredHeight() +{ + S32 rec_height = 0; + + std::vector::iterator panel; + for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(*panel); + if(accordion_tab && accordion_tab->getVisible()) + { + rec_height += accordion_tab->getRect().getHeight(); + } + } + + mInnerRect.setLeftTopAndSize(0, rec_height + BORDER_MARGIN * 2, getRect().getWidth(), rec_height + BORDER_MARGIN); + + return mInnerRect.getHeight(); +} + +//--------------------------------------------------------------------------------- +void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height) +{ + if (!panel) + return; + LLRect panel_rect = panel->getRect(); + panel_rect.setLeftTopAndSize( left, top, width, height); + panel->reshape( width, height, 1); + panel->setRect(panel_rect); +} + +void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta) +{ + if (!panel) + return; + panel->translate(0,delta); +} + +//--------------------------------------------------------------------------------- + +void LLAccordionCtrl::addCollapsibleCtrl(LLView* view) +{ + LLAccordionCtrlTab* accordion_tab = dynamic_cast(view); + if (!accordion_tab) + return; + if (std::find(beginChild(), endChild(), accordion_tab) == endChild()) + addChild(accordion_tab); + mAccordionTabs.push_back(accordion_tab); + + accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, (S16)(mAccordionTabs.size() - 1)) ); + arrange(); +} + +void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view) +{ + LLAccordionCtrlTab* accordion_tab = dynamic_cast(view); + if(!accordion_tab) + return; + + if(std::find(beginChild(), endChild(), accordion_tab) != endChild()) + removeChild(accordion_tab); + + for (std::vector::iterator iter = mAccordionTabs.begin(); + iter != mAccordionTabs.end(); ++iter) + { + if (accordion_tab == (*iter)) + { + mAccordionTabs.erase(iter); + break; + } + } + + // if removed is selected - reset selection + if (mSelectedTab == view) + { + mSelectedTab = NULL; + } +} + +void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params) +{ + LLTextBox::Params tp = tb_params; + tp.rect(getLocalRect()); + mNoMatchedTabsOrigString = tp.initial_value().asString(); + mNoVisibleTabsHelpText = LLUICtrlFactory::create(tp, this); +} + +void LLAccordionCtrl::updateNoTabsHelpTextVisibility() +{ + bool visible_exists = false; + std::vector::const_iterator it = mAccordionTabs.begin(); + const std::vector::const_iterator it_end = mAccordionTabs.end(); + while (it < it_end) + { + if ((*(it++))->getVisible()) + { + visible_exists = true; + break; + } + } + + mNoVisibleTabsHelpText->setVisible(!visible_exists); +} + +void LLAccordionCtrl::arrangeSingle() +{ + S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter + S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel + S32 panel_width = getRect().getWidth() - 4; + S32 panel_height; + + S32 collapsed_height = 0; + + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); + + if (!accordion_tab->getVisible()) // Skip hidden accordion tabs + continue; + if (!accordion_tab->isExpanded() ) + { + collapsed_height+=mAccordionTabs[i]->getRect().getHeight(); + } + } + + S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height; + + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); + + if (!accordion_tab->getVisible()) // Skip hidden accordion tabs + continue; + if (!accordion_tab->isExpanded() ) + { + panel_height = accordion_tab->getRect().getHeight(); + } + else + { + if (mFitParent) + { + panel_height = expanded_height; + } + else + { + if (accordion_tab->getAccordionView()) + { + panel_height = accordion_tab->getAccordionView()->getRect().getHeight() + + accordion_tab->getHeaderHeight() + BORDER_MARGIN * 2; + } + else + { + panel_height = accordion_tab->getRect().getHeight(); + } + } + } + + // make sure at least header is shown + panel_height = llmax(panel_height, accordion_tab->getHeaderHeight()); + + ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); + panel_top -= mAccordionTabs[i]->getRect().getHeight(); + } + + show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); + updateLayout(getRect().getWidth(), getRect().getHeight()); +} + +void LLAccordionCtrl::arrangeMultiple() +{ + S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter + S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel + S32 panel_width = getRect().getWidth() - 4; + + //Calculate params + for (size_t i = 0; i < mAccordionTabs.size(); i++ ) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); + + if (!accordion_tab->getVisible()) // Skip hidden accordion tabs + continue; + + if (!accordion_tab->isExpanded() ) + { + ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight()); + panel_top -= mAccordionTabs[i]->getRect().getHeight(); + } + else + { + S32 panel_height = accordion_tab->getRect().getHeight(); + + if (mFitParent) + { + // All expanded tabs will have equal height + panel_height = calcExpandedTabHeight(i, panel_top); + ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height); + + // Try to make accordion tab fit accordion view height. + // Accordion View should implement getRequiredRect() and provide valid height + S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight(); + optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN; + if (optimal_height < panel_height) + { + panel_height = optimal_height; + } + + // minimum tab height is equal to header height + if (mAccordionTabs[i]->getHeaderHeight() > panel_height) + { + panel_height = mAccordionTabs[i]->getHeaderHeight(); + } + } + + ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); + panel_top -= panel_height; + + } + } + + show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); + + updateLayout(getRect().getWidth(), getRect().getHeight()); +} + + +void LLAccordionCtrl::arrange() +{ + updateNoTabsHelpTextVisibility(); + + if (mAccordionTabs.empty()) + { + // Nothing to arrange + return; + } + + if (mAccordionTabs.size() == 1) + { + S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel + S32 panel_width = getRect().getWidth() - 4; + + LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[0]); + + LLRect panel_rect = accordion_tab->getRect(); + + S32 panel_height = getRect().getHeight() - BORDER_MARGIN * 2; + if (accordion_tab->getFitParent()) + panel_height = accordion_tab->getRect().getHeight(); + + ctrlSetLeftTopAndSize(accordion_tab, panel_rect.mLeft, panel_top, panel_width, panel_height); + + show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); + return; + } + + if (mSingleExpansion) + arrangeSingle(); + else + arrangeMultiple(); +} + +//--------------------------------------------------------------------------------- + +bool LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if (LLPanel::handleScrollWheel(x, y, clicks)) + return true; + if (mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) + return true; + return false; +} + +bool LLAccordionCtrl::handleKeyHere(KEY key, MASK mask) +{ + if (mScrollbar->getVisible() && mScrollbar->handleKeyHere(key, mask)) + return true; + return LLPanel::handleKeyHere(key, mask); +} + +bool LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + // Scroll folder view if needed. Never accepts a drag or drop. + *accept = ACCEPT_NO; + bool handled = autoScroll(x, y); + + if (!handled) + { + handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, + cargo_data, accept, tooltip_msg) != NULL; + } + return true; +} + +bool LLAccordionCtrl::autoScroll(S32 x, S32 y) +{ + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + bool scrolling = false; + if (mScrollbar->getVisible()) + { + LLRect rect_local(0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0); + LLRect screen_local_extents; + + // clip rect against root view + screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents); + rect_local.intersectWith(screen_local_extents); + + // autoscroll region should take up no more than one third of visible scroller area + S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10); + S32 auto_scroll_speed = ll_round(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); + + LLRect bottom_scroll_rect = screen_local_extents; + bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height; + if (bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax())) + { + mScrollbar->setDocPos(mScrollbar->getDocPos() + auto_scroll_speed); + mAutoScrolling = true; + scrolling = true; + } + + LLRect top_scroll_rect = screen_local_extents; + top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height; + if (top_scroll_rect.pointInRect(x, y) && (mScrollbar->getDocPos() > 0)) + { + mScrollbar->setDocPos(mScrollbar->getDocPos() - auto_scroll_speed); + mAutoScrolling = true; + scrolling = true; + } + } + + return scrolling; +} + +void LLAccordionCtrl::updateLayout(S32 width, S32 height) +{ + S32 panel_top = height - BORDER_MARGIN ; + if (mScrollbar->getVisible()) + panel_top += mScrollbar->getDocPos(); + + S32 panel_width = width - BORDER_MARGIN * 2; + + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + if (mScrollbar->getVisible()) + panel_width -= scrollbar_size; + + // set sizes for first panels and dragbars + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + if (!mAccordionTabs[i]->getVisible()) + continue; + LLRect panel_rect = mAccordionTabs[i]->getRect(); + ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_top, panel_width, panel_rect.getHeight()); + panel_top -= panel_rect.getHeight(); + } +} + +void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*) +{ + updateLayout(getRect().getWidth(), getRect().getHeight()); +} + +// virtual +void LLAccordionCtrl::onUpdateScrollToChild(const LLUICtrl *cntrl) +{ + if (mScrollbar && mScrollbar->getVisible() && !mSkipScrollToChild) + { + // same as scrollToShowRect + LLRect rect; + cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this); + + // Translate to parent coordinatess to check if we are in visible rectangle + rect.translate(getRect().mLeft, getRect().mBottom); + + if (!getRect().contains(rect)) + { + // for accordition's scroll, height is in pixels + // Back to local coords and calculate position for scroller + S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom; + S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop; + + S32 scroll_pos = llclamp(mScrollbar->getDocPos(), + bottom, // min vertical scroll + top); // max vertical scroll + + mScrollbar->setDocPos(scroll_pos); + } + } + + LLUICtrl::onUpdateScrollToChild(cntrl); +} + +void LLAccordionCtrl::onOpen(const LLSD& key) +{ + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); + LLPanel* panel = dynamic_cast(accordion_tab->getAccordionView()); + if (panel != NULL) + { + panel->onOpen(key); + } + } +} + +S32 LLAccordionCtrl::notifyParent(const LLSD& info) +{ + if (info.has("action")) + { + std::string str_action = info["action"]; + if (str_action == "size_changes") + { + // + arrange(); + return 1; + } + if (str_action == "select_next") + { + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); + if (accordion_tab->hasFocus()) + { + while (++i < mAccordionTabs.size()) + { + if (mAccordionTabs[i]->getVisible()) + break; + } + if (i < mAccordionTabs.size()) + { + accordion_tab = dynamic_cast(mAccordionTabs[i]); + accordion_tab->notify(LLSD().with("action","select_first")); + return 1; + } + break; + } + } + return 0; + } + if (str_action == "select_prev") + { + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); + if (accordion_tab->hasFocus() && i > 0) + { + bool prev_visible_tab_found = false; + while (i > 0) + { + if (mAccordionTabs[--i]->getVisible()) + { + prev_visible_tab_found = true; + break; + } + } + + if (prev_visible_tab_found) + { + accordion_tab = dynamic_cast(mAccordionTabs[i]); + accordion_tab->notify(LLSD().with("action","select_last")); + return 1; + } + break; + } + } + return 0; + } + if (str_action == "select_current") + { + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + // Set selection to the currently focused tab. + if (mAccordionTabs[i]->hasFocus()) + { + if (mAccordionTabs[i] != mSelectedTab) + { + if (mSelectedTab) + { + mSelectedTab->setSelected(false); + } + mSelectedTab = mAccordionTabs[i]; + mSelectedTab->setSelected(true); + } + + return 1; + } + } + return 0; + } + if (str_action == "deselect_current") + { + // Reset selection to the currently selected tab. + if (mSelectedTab) + { + mSelectedTab->setSelected(false); + mSelectedTab = NULL; + return 1; + } + return 0; + } + } + else if (info.has("scrollToShowRect")) + { + LLRect screen_rc, local_rc; + screen_rc.setValue(info["scrollToShowRect"]); + screenRectToLocal(screen_rc, &local_rc); + + // Translate to parent coordinatess to check if we are in visible rectangle + local_rc.translate(getRect().mLeft, getRect().mBottom); + + if (!getRect().contains (local_rc)) + { + // Back to local coords and calculate position for scroller + S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom; + S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop; + + S32 scroll_pos = llclamp(mScrollbar->getDocPos(), + bottom, // min vertical scroll + top); // max vertical scroll + + mScrollbar->setDocPos(scroll_pos); + } + return 1; + } + else if (info.has("child_visibility_change")) + { + bool new_visibility = info["child_visibility_change"]; + if (new_visibility) + { + // there is at least one visible tab + mNoVisibleTabsHelpText->setVisible(false); + } + else + { + // it could be the latest visible tab, check all of them + updateNoTabsHelpTextVisibility(); + } + } + return LLPanel::notifyParent(info); +} + +void LLAccordionCtrl::reset() +{ + if (mScrollbar) + mScrollbar->setDocPos(0); +} + +void LLAccordionCtrl::expandDefaultTab() +{ + if (!mAccordionTabs.empty()) + { + LLAccordionCtrlTab* tab = mAccordionTabs.front(); + + if (!tab->getDisplayChildren()) + { + tab->setDisplayChildren(true); + } + + for (size_t i = 1; i < mAccordionTabs.size(); ++i) + { + tab = mAccordionTabs[i]; + + if (tab->getDisplayChildren()) + { + tab->setDisplayChildren(false); + } + } + + arrange(); + } +} + +void LLAccordionCtrl::sort() +{ + if (!mTabComparator) + { + LL_WARNS() << "No comparator specified for sorting accordion tabs." << LL_ENDL; + return; + } + + std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator)); + arrange(); +} + +void LLAccordionCtrl::setFilterSubString(const std::string& filter_string) +{ + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(filter_string); + std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString; + LLStringUtil::format(text, args); + + mNoVisibleTabsHelpText->setValue(text); +} + +const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const +{ + typedef std::vector::const_iterator tabs_const_iterator; + + const LLAccordionCtrlTab* result = 0; + + for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i) + { + if ((*i)->isExpanded()) + { + result = *i; + break; + } + } + + return result; +} + +S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */) +{ + if (tab_index < 0) + { + return available_height; + } + + S32 collapsed_tabs_height = 0; + S32 num_expanded = 0; + + for (size_t n = tab_index; n < mAccordionTabs.size(); ++n) + { + if (!mAccordionTabs[n]->isExpanded()) + { + collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight(); + } + else + { + ++num_expanded; + } + } + + if (0 == num_expanded) + { + return available_height; + } + + S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN + expanded_tab_height /= num_expanded; + return expanded_tab_height; +} + +void LLAccordionCtrl::collapseAllTabs() +{ + if (mAccordionTabs.size() > 0) + { + for (size_t i = 0; i < mAccordionTabs.size(); ++i) + { + LLAccordionCtrlTab *tab = mAccordionTabs[i]; + + if (tab->getDisplayChildren()) + { + tab->setDisplayChildren(false); + } + } + arrange(); + } +} diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h index baeed0a3a0..1dfa9100f6 100644 --- a/indra/llui/llaccordionctrl.h +++ b/indra/llui/llaccordionctrl.h @@ -1,200 +1,200 @@ -/** - * @file LLAccordionCtrl.h - * @brief Accordion Panel implementation - * - * $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_ACCORDIONCTRL_H -#define LL_ACCORDIONCTRL_H - -#include "llpanel.h" -#include "lltextbox.h" -#include "llscrollbar.h" - -#include -#include -#include - -class LLAccordionCtrlTab; - -class LLAccordionCtrl: public LLPanel -{ -private: - - std::vector mAccordionTabs; - - void ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height); - void ctrlShiftVertical(LLView* panel,S32 delta); - - void onCollapseCtrlCloseOpen(S16 panel_num); - void shiftAccordionTabs(S16 panel_num, S32 delta); - - -public: - /** - * Abstract comparator for accordion tabs. - */ - class LLTabComparator - { - public: - LLTabComparator() {}; - virtual ~LLTabComparator() {}; - - /** Returns true if tab1 < tab2, false otherwise */ - virtual bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const = 0; - }; - - struct Params - : public LLInitParam::Block - { - Optional single_expansion, - fit_parent; /* Accordion will fit its parent size, controls that are placed into - accordion tabs are responsible for scrolling their content. - *NOTE fit_parent works best when combined with single_expansion. - Accordion view should implement getRequiredRect() and provide valid height*/ - Optional no_matched_tabs_text; - Optional no_visible_tabs_text; - - Params() - : single_expansion("single_expansion",false) - , fit_parent("fit_parent", false) - , no_matched_tabs_text("no_matched_tabs_text") - , no_visible_tabs_text("no_visible_tabs_text") - {}; - }; - - LLAccordionCtrl(const Params& params); - - LLAccordionCtrl(); - virtual ~LLAccordionCtrl(); - - virtual bool postBuild(); - - virtual bool handleRightMouseDown ( S32 x, S32 y, MASK mask); - virtual bool handleScrollWheel ( S32 x, S32 y, S32 clicks ); - virtual bool handleKeyHere (KEY key, 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); - // - - // Call reshape after changing splitter's size - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - - void addCollapsibleCtrl(LLView* view); - void removeCollapsibleCtrl(LLView* view); - void arrange(); - - - void draw(); - - void onScrollPosChangeCallback(S32, LLScrollbar*); - virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); - - void onOpen (const LLSD& key); - S32 notifyParent(const LLSD& info); - - void reset (); - void expandDefaultTab(); - - void setComparator(const LLTabComparator* comp) { mTabComparator = comp; } - void sort(); - - void collapseAllTabs(); - - /** - * Sets filter substring as a search_term for help text when there are no any visible tabs. - */ - void setFilterSubString(const std::string& filter_string); - - /** - * This method returns the first expanded accordion tab. - * It is expected to be called for accordion which doesn't allow multiple - * tabs to be expanded. Use with care. - */ - const LLAccordionCtrlTab* getExpandedTab() const; - - LLAccordionCtrlTab* getSelectedTab() const { return mSelectedTab; } - - bool getFitParent() const {return mFitParent;} - - void setSkipScrollToChild(bool skip) { mSkipScrollToChild = skip; } - -private: - void initNoTabsWidget(const LLTextBox::Params& tb_params); - void updateNoTabsHelpTextVisibility(); - - void arrangeSingle(); - void arrangeMultiple(); - - // Calc Splitter's height that is necessary to display all child content - S32 calcRecuiredHeight(); - S32 getRecuiredHeight() const { return mInnerRect.getHeight(); } - S32 calcExpandedTabHeight(S32 tab_index = 0, S32 available_height = 0); - - void updateLayout (S32 width, S32 height); - - void show_hide_scrollbar (S32 width, S32 height); - - void showScrollbar (S32 width, S32 height); - void hideScrollbar (S32 width, S32 height); - - bool autoScroll (S32 x, S32 y); - - /** - * An adaptor for LLTabComparator - */ - struct LLComparatorAdaptor - { - LLComparatorAdaptor(const LLTabComparator& comparator) : mComparator(comparator) {}; - - bool operator()(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) - { - return mComparator.compare(tab1, tab2); - } - - const LLTabComparator& mComparator; - }; - -private: - LLRect mInnerRect; - LLScrollbar* mScrollbar; - bool mSingleExpansion; - bool mFitParent; - bool mAutoScrolling; - F32 mAutoScrollRate; - LLTextBox* mNoVisibleTabsHelpText; - - bool mSkipScrollToChild; - - std::string mNoMatchedTabsOrigString; - std::string mNoVisibleTabsOrigString; - - LLAccordionCtrlTab* mSelectedTab; - const LLTabComparator* mTabComparator; -}; - - -#endif // LL_LLSPLITTER_H +/** + * @file LLAccordionCtrl.h + * @brief Accordion Panel implementation + * + * $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_ACCORDIONCTRL_H +#define LL_ACCORDIONCTRL_H + +#include "llpanel.h" +#include "lltextbox.h" +#include "llscrollbar.h" + +#include +#include +#include + +class LLAccordionCtrlTab; + +class LLAccordionCtrl: public LLPanel +{ +private: + + std::vector mAccordionTabs; + + void ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height); + void ctrlShiftVertical(LLView* panel,S32 delta); + + void onCollapseCtrlCloseOpen(S16 panel_num); + void shiftAccordionTabs(S16 panel_num, S32 delta); + + +public: + /** + * Abstract comparator for accordion tabs. + */ + class LLTabComparator + { + public: + LLTabComparator() {}; + virtual ~LLTabComparator() {}; + + /** Returns true if tab1 < tab2, false otherwise */ + virtual bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const = 0; + }; + + struct Params + : public LLInitParam::Block + { + Optional single_expansion, + fit_parent; /* Accordion will fit its parent size, controls that are placed into + accordion tabs are responsible for scrolling their content. + *NOTE fit_parent works best when combined with single_expansion. + Accordion view should implement getRequiredRect() and provide valid height*/ + Optional no_matched_tabs_text; + Optional no_visible_tabs_text; + + Params() + : single_expansion("single_expansion",false) + , fit_parent("fit_parent", false) + , no_matched_tabs_text("no_matched_tabs_text") + , no_visible_tabs_text("no_visible_tabs_text") + {}; + }; + + LLAccordionCtrl(const Params& params); + + LLAccordionCtrl(); + virtual ~LLAccordionCtrl(); + + virtual bool postBuild(); + + virtual bool handleRightMouseDown ( S32 x, S32 y, MASK mask); + virtual bool handleScrollWheel ( S32 x, S32 y, S32 clicks ); + virtual bool handleKeyHere (KEY key, 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); + // + + // Call reshape after changing splitter's size + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + + void addCollapsibleCtrl(LLView* view); + void removeCollapsibleCtrl(LLView* view); + void arrange(); + + + void draw(); + + void onScrollPosChangeCallback(S32, LLScrollbar*); + virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); + + void onOpen (const LLSD& key); + S32 notifyParent(const LLSD& info); + + void reset (); + void expandDefaultTab(); + + void setComparator(const LLTabComparator* comp) { mTabComparator = comp; } + void sort(); + + void collapseAllTabs(); + + /** + * Sets filter substring as a search_term for help text when there are no any visible tabs. + */ + void setFilterSubString(const std::string& filter_string); + + /** + * This method returns the first expanded accordion tab. + * It is expected to be called for accordion which doesn't allow multiple + * tabs to be expanded. Use with care. + */ + const LLAccordionCtrlTab* getExpandedTab() const; + + LLAccordionCtrlTab* getSelectedTab() const { return mSelectedTab; } + + bool getFitParent() const {return mFitParent;} + + void setSkipScrollToChild(bool skip) { mSkipScrollToChild = skip; } + +private: + void initNoTabsWidget(const LLTextBox::Params& tb_params); + void updateNoTabsHelpTextVisibility(); + + void arrangeSingle(); + void arrangeMultiple(); + + // Calc Splitter's height that is necessary to display all child content + S32 calcRecuiredHeight(); + S32 getRecuiredHeight() const { return mInnerRect.getHeight(); } + S32 calcExpandedTabHeight(S32 tab_index = 0, S32 available_height = 0); + + void updateLayout (S32 width, S32 height); + + void show_hide_scrollbar (S32 width, S32 height); + + void showScrollbar (S32 width, S32 height); + void hideScrollbar (S32 width, S32 height); + + bool autoScroll (S32 x, S32 y); + + /** + * An adaptor for LLTabComparator + */ + struct LLComparatorAdaptor + { + LLComparatorAdaptor(const LLTabComparator& comparator) : mComparator(comparator) {}; + + bool operator()(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) + { + return mComparator.compare(tab1, tab2); + } + + const LLTabComparator& mComparator; + }; + +private: + LLRect mInnerRect; + LLScrollbar* mScrollbar; + bool mSingleExpansion; + bool mFitParent; + bool mAutoScrolling; + F32 mAutoScrollRate; + LLTextBox* mNoVisibleTabsHelpText; + + bool mSkipScrollToChild; + + std::string mNoMatchedTabsOrigString; + std::string mNoVisibleTabsOrigString; + + LLAccordionCtrlTab* mSelectedTab; + const LLTabComparator* mTabComparator; +}; + + +#endif // LL_LLSPLITTER_H diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 17f9da8707..6d58a2545c 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -1,1126 +1,1126 @@ -/** - * @file LLAccordionCtrlTab.cpp - * @brief Collapsible control implementation - * - * $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 "llaccordionctrltab.h" -#include "llaccordionctrl.h" - -#include "lllocalcliprect.h" -#include "llscrollbar.h" -#include "lltextbox.h" -#include "lltextutil.h" -#include "lluictrl.h" - -static const std::string DD_BUTTON_NAME = "dd_button"; -static const std::string DD_TEXTBOX_NAME = "dd_textbox"; -static const std::string DD_HEADER_NAME = "dd_header"; - -static const S32 HEADER_HEIGHT = 23; -static const S32 HEADER_IMAGE_LEFT_OFFSET = 5; -static const S32 HEADER_TEXT_LEFT_OFFSET = 30; -static const F32 AUTO_OPEN_TIME = 1.f; -static const S32 VERTICAL_MULTIPLE = 16; -static const S32 PARENT_BORDER_MARGIN = 5; - -static LLDefaultChildRegistry::Register t1("accordion_tab"); - -class LLAccordionCtrlTab::LLAccordionCtrlTabHeader : public LLUICtrl -{ -public: - friend class LLUICtrlFactory; - - struct Params : public LLInitParam::Block - { - Params(); - }; - - LLAccordionCtrlTabHeader(const LLAccordionCtrlTabHeader::Params& p); - - virtual ~LLAccordionCtrlTabHeader(); - - virtual void draw(); - - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - - virtual bool postBuild(); - - std::string getTitle(); - void setTitle(const std::string& title, const std::string& hl); - - void setTitleFontStyle(std::string style); - - void setTitleColor(LLUIColor); - - void setSelected(bool is_selected) { mIsSelected = is_selected; } - - virtual void onMouseEnter(S32 x, S32 y, MASK mask); - virtual void onMouseLeave(S32 x, S32 y, MASK mask); - virtual bool handleKey(KEY key, MASK mask, 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); - -private: - LLTextBox* mHeaderTextbox; - - // Overlay images (arrows) - LLPointer mImageCollapsed; - LLPointer mImageExpanded; - LLPointer mImageCollapsedPressed; - LLPointer mImageExpandedPressed; - - // Background images - LLPointer mImageHeader; - LLPointer mImageHeaderOver; - LLPointer mImageHeaderPressed; - LLPointer mImageHeaderFocused; - - // style saved when applying it in setTitleFontStyle - LLStyle::Params mStyleParams; - - LLUIColor mHeaderBGColor; - - bool mNeedsHighlight; - bool mIsSelected; - - LLFrameTimer mAutoOpenTimer; -}; - -LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params() -{ -} - -LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader( - const LLAccordionCtrlTabHeader::Params& p) -: LLUICtrl(p) -, mHeaderBGColor(p.header_bg_color()) -, mNeedsHighlight(false) -, mIsSelected(false), - mImageCollapsed(p.header_collapse_img), - mImageCollapsedPressed(p.header_collapse_img_pressed), - mImageExpanded(p.header_expand_img), - mImageExpandedPressed(p.header_expand_img_pressed), - mImageHeader(p.header_image), - mImageHeaderOver(p.header_image_over), - mImageHeaderPressed(p.header_image_pressed), - mImageHeaderFocused(p.header_image_focused) -{ - LLTextBox::Params textboxParams; - textboxParams.name(DD_TEXTBOX_NAME); - textboxParams.initial_value(p.title()); - textboxParams.text_color(p.header_text_color()); - textboxParams.follows.flags(FOLLOWS_NONE); - textboxParams.font( p.font() ); - textboxParams.font_shadow(LLFontGL::NO_SHADOW); - textboxParams.use_ellipses = true; - textboxParams.bg_visible = false; - textboxParams.mouse_opaque = false; - textboxParams.parse_urls = false; - mHeaderTextbox = LLUICtrlFactory::create(textboxParams); - addChild(mHeaderTextbox); -} - -LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader() -{ -} - -bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild() -{ - return true; -} - -std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle() -{ - if (mHeaderTextbox) - { - return mHeaderTextbox->getText(); - } - - return LLStringUtil::null; -} - -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl) -{ - if (mHeaderTextbox) - { - LLTextUtil::textboxSetHighlightedVal( - mHeaderTextbox, - mStyleParams, - title, - hl); - } -} - -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string style) -{ - if (mHeaderTextbox) - { - std::string text = mHeaderTextbox->getText(); - mStyleParams.font(mHeaderTextbox->getFont()); - mStyleParams.font.style(style); - mHeaderTextbox->setText(text, mStyleParams); - } -} - -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleColor(LLUIColor color) -{ - if (mHeaderTextbox) - { - mHeaderTextbox->setColor(color); - } -} - -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() -{ - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); - - F32 alpha = getCurrentTransparency(); - gl_rect_2d(0, 0, width - 1, height - 1, mHeaderBGColor.get() % alpha, true); - - LLAccordionCtrlTab* parent = dynamic_cast(getParent()); - bool collapsible = parent && parent->getCollapsible(); - bool expanded = parent && parent->getDisplayChildren(); - - // Handle overlay images, if needed - // Only show green "focus" background image if the accordion is open, - // because the user's mental model of focus is that it goes away after - // the accordion is closed. - if (getParent()->hasFocus() || mIsSelected - /*&& !(collapsible && !expanded)*/ // WHY?? - ) - { - mImageHeaderFocused->draw(0, 0, width, height); - } - else - { - mImageHeader->draw(0, 0, width, height); - } - - if (mNeedsHighlight) - { - mImageHeaderOver->draw(0, 0, width, height); - } - - if (collapsible) - { - LLPointer overlay_image; - if (expanded) - { - overlay_image = mImageExpanded; - } - else - { - overlay_image = mImageCollapsed; - } - overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET, (height - overlay_image->getHeight()) / 2); - } - - LLUICtrl::draw(); -} - -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */) -{ - S32 header_height = mHeaderTextbox->getTextPixelHeight(); - - LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2); - mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight()); - mHeaderTextbox->setRect(textboxRect); - - if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth()) - { - setToolTip(mHeaderTextbox->getText()); - } - else - { - setToolTip(LLStringUtil::null); - } -} - -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MASK mask) -{ - LLUICtrl::onMouseEnter(x, y, mask); - mNeedsHighlight = true; -} - -void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask) -{ - LLUICtrl::onMouseLeave(x, y, mask); - mNeedsHighlight = false; - mAutoOpenTimer.stop(); -} - -bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - if ((key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE) - { - return getParent()->handleKey(key, mask, called_from_parent); - } - - return LLUICtrl::handleKey(key, mask, called_from_parent); -} - -bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask, - bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - LLAccordionCtrlTab* parent = dynamic_cast(getParent()); - - if (parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose()) - { - if (mAutoOpenTimer.getStarted()) - { - if (mAutoOpenTimer.getElapsedTimeF32() > AUTO_OPEN_TIME) - { - parent->changeOpenClose(false); - mAutoOpenTimer.stop(); - return true; - } - } - else - { - mAutoOpenTimer.start(); - } - } - - return LLUICtrl::handleDragAndDrop(x, y, mask, drop, cargo_type, - cargo_data, accept, tooltip_msg); -} - -LLAccordionCtrlTab::Params::Params() - : title("title") - ,display_children("expanded", true) - ,header_height("header_height", HEADER_HEIGHT), - min_width("min_width", 0), - min_height("min_height", 0) - ,collapsible("collapsible", true) - ,header_bg_color("header_bg_color") - ,dropdown_bg_color("dropdown_bg_color") - ,header_visible("header_visible",true) - ,padding_left("padding_left",2) - ,padding_right("padding_right",2) - ,padding_top("padding_top",2) - ,padding_bottom("padding_bottom",2) - ,header_expand_img("header_expand_img") - ,header_expand_img_pressed("header_expand_img_pressed") - ,header_collapse_img("header_collapse_img") - ,header_collapse_img_pressed("header_collapse_img_pressed") - ,header_image("header_image") - ,header_image_over("header_image_over") - ,header_image_pressed("header_image_pressed") - ,header_image_focused("header_image_focused") - ,header_text_color("header_text_color") - ,fit_panel("fit_panel",true) - ,selection_enabled("selection_enabled", false) -{ - changeDefault(mouse_opaque, false); -} - -LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p) - : LLUICtrl(p) - ,mDisplayChildren(p.display_children) - ,mCollapsible(p.collapsible) - ,mExpandedHeight(0) - ,mDropdownBGColor(p.dropdown_bg_color()) - ,mHeaderVisible(p.header_visible) - ,mPaddingLeft(p.padding_left) - ,mPaddingRight(p.padding_right) - ,mPaddingTop(p.padding_top) - ,mPaddingBottom(p.padding_bottom) - ,mCanOpenClose(true) - ,mFitPanel(p.fit_panel) - ,mSelectionEnabled(p.selection_enabled) - ,mContainerPanel(NULL) - ,mScrollbar(NULL) -{ - mStoredOpenCloseState = false; - mWasStateStored = false; - mSkipChangesOnNotifyParent = false; - - mDropdownBGColor = LLColor4::white; - LLAccordionCtrlTabHeader::Params headerParams; - headerParams.name(DD_HEADER_NAME); - headerParams.title(p.title); - mHeader = LLUICtrlFactory::create(headerParams); - addChild(mHeader, 1); - - LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLAccordionCtrlTab::selectOnFocusReceived, this)); - - if (!p.selection_enabled) - { - LLFocusableElement::setFocusLostCallback(boost::bind(&LLAccordionCtrlTab::deselectOnFocusLost, this)); - } - - reshape(100, 200,false); -} - -LLAccordionCtrlTab::~LLAccordionCtrlTab() -{ -} - -void LLAccordionCtrlTab::setDisplayChildren(bool display) -{ - mDisplayChildren = display; - LLRect rect = getRect(); - - rect.mBottom = rect.mTop - (getDisplayChildren() ? mExpandedHeight : HEADER_HEIGHT); - setRect(rect); - - if (mContainerPanel) - { - mContainerPanel->setVisible(getDisplayChildren()); - } - - if (mDisplayChildren) - { - adjustContainerPanel(); - } - else - { - if (mScrollbar) - mScrollbar->setVisible(false); - } -} - -void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent /* = true */) -{ - LLRect headerRect; - - headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT); - mHeader->setRect(headerRect); - mHeader->reshape(headerRect.getWidth(), headerRect.getHeight()); - - if (!mDisplayChildren) - return; - - LLRect childRect; - - childRect.setLeftTopAndSize( - getPaddingLeft(), - height - getHeaderHeight() - getPaddingTop(), - width - getPaddingLeft() - getPaddingRight(), - height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); - - adjustContainerPanel(childRect); -} - -void LLAccordionCtrlTab::changeOpenClose(bool is_open) -{ - if (is_open) - mExpandedHeight = getRect().getHeight(); - - setDisplayChildren(!is_open); - reshape(getRect().getWidth(), getRect().getHeight(), false); - if (mCommitSignal) - { - (*mCommitSignal)(this, getDisplayChildren()); - } -} - -void LLAccordionCtrlTab::onVisibilityChange(bool new_visibility) -{ - LLUICtrl::onVisibilityChange(new_visibility); - - notifyParent(LLSD().with("child_visibility_change", new_visibility)); -} - -// virtual -void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl) -{ - if (mScrollbar && mScrollbar->getVisible()) - { - LLRect rect; - cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this); - - // Translate to parent coordinatess to check if we are in visible rectangle - rect.translate(getRect().mLeft, getRect().mBottom); - - if (!getRect().contains(rect)) - { - // for accordition's scroll, height is in pixels - // Back to local coords and calculate position for scroller - S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom; - S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop; - - S32 scroll_pos = llclamp(mScrollbar->getDocPos(), - bottom, // min vertical scroll - top); // max vertical scroll - - mScrollbar->setDocPos(scroll_pos); - } - } - - LLUICtrl::onUpdateScrollToChild(cntrl); -} - -bool LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (mCollapsible && mHeaderVisible && mCanOpenClose) - { - if (y >= (getRect().getHeight() - HEADER_HEIGHT)) - { - mHeader->setFocus(true); - changeOpenClose(getDisplayChildren()); - - // Reset stored state - mWasStateStored = false; - return true; - } - } - return LLUICtrl::handleMouseDown(x,y,mask); -} - -bool LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask) -{ - return LLUICtrl::handleMouseUp(x,y,mask); -} - -boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback(commit_callback_t cb) -{ - return setCommitCallback(cb); -} - -bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group) -{ - if (DD_HEADER_NAME != child->getName()) - { - reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT ); - mExpandedHeight = getRect().getHeight(); - } - - bool res = LLUICtrl::addChild(child, tab_group); - - if (DD_HEADER_NAME != child->getName()) - { - if (!mCollapsible) - setDisplayChildren(true); - else - setDisplayChildren(getDisplayChildren()); - } - - if (!mContainerPanel) - mContainerPanel = findContainerView(); - - return res; -} - -void LLAccordionCtrlTab::setAccordionView(LLView* panel) -{ - addChild(panel, 0); -} - -std::string LLAccordionCtrlTab::getTitle() const -{ - if (mHeader) - { - return mHeader->getTitle(); - } - - return LLStringUtil::null; -} - -void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl) -{ - if (mHeader) - { - mHeader->setTitle(title, hl); - } -} - -void LLAccordionCtrlTab::setTitleFontStyle(std::string style) -{ - if (mHeader) - { - mHeader->setTitleFontStyle(style); - } -} - -void LLAccordionCtrlTab::setTitleColor(LLUIColor color) -{ - if (mHeader) - { - mHeader->setTitleColor(color); - } -} - -boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb) -{ - if (mHeader) - { - return mHeader->setFocusReceivedCallback(cb); - } - - return boost::signals2::connection(); -} - -boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb) -{ - if (mHeader) - { - return mHeader->setFocusLostCallback(cb); - } - - return boost::signals2::connection(); -} - -void LLAccordionCtrlTab::setSelected(bool is_selected) -{ - if (mHeader) - { - mHeader->setSelected(is_selected); - } -} - -LLView* LLAccordionCtrlTab::findContainerView() -{ - child_list_const_iter_t it = getChildList()->begin(), it_end = getChildList()->end(); - while (it != it_end) - { - LLView* child = *(it++); - if (DD_HEADER_NAME != child->getName() && child->getVisible()) - return child; - } - - return NULL; -} - -void LLAccordionCtrlTab::selectOnFocusReceived() -{ - if (getParent()) // A parent may not be set if tabs are added dynamically. - { - getParent()->notifyParent(LLSD().with("action", "select_current")); - } -} - -void LLAccordionCtrlTab::deselectOnFocusLost() -{ - if (getParent()) // A parent may not be set if tabs are added dynamically. - { - getParent()->notifyParent(LLSD().with("action", "deselect_current")); - } -} - -S32 LLAccordionCtrlTab::getHeaderHeight() -{ - return mHeaderVisible ? HEADER_HEIGHT : 0; -} - -void LLAccordionCtrlTab::setHeaderVisible(bool value) -{ - if (mHeaderVisible == value) - return; - - mHeaderVisible = value; - - if (mHeader) - { - mHeader->setVisible(value); - } - - reshape(getRect().getWidth(), getRect().getHeight(), false); -}; - -//virtual -bool LLAccordionCtrlTab::postBuild() -{ - if (mHeader) - { - mHeader->setVisible(mHeaderVisible); - } - - static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); - - LLRect scroll_rect; - scroll_rect.setOriginAndSize( - getRect().getWidth() - scrollbar_size, - 1, - scrollbar_size, - getRect().getHeight() - 1); - - mContainerPanel = findContainerView(); - - if (!mFitPanel) - { - LLScrollbar::Params sbparams; - sbparams.name("scrollable vertical"); - sbparams.rect(scroll_rect); - sbparams.orientation(LLScrollbar::VERTICAL); - sbparams.doc_size(getRect().getHeight()); - sbparams.doc_pos(0); - sbparams.page_size(getRect().getHeight()); - sbparams.step_size(VERTICAL_MULTIPLE); - sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); - sbparams.change_callback(boost::bind(&LLAccordionCtrlTab::onScrollPosChangeCallback, this, _1, _2)); - - mScrollbar = LLUICtrlFactory::create(sbparams); - LLView::addChild(mScrollbar); - mScrollbar->setFollowsRight(); - mScrollbar->setFollowsTop(); - mScrollbar->setFollowsBottom(); - - mScrollbar->setVisible(false); - } - - if (mContainerPanel) - { - mContainerPanel->setVisible(mDisplayChildren); - } - - return LLUICtrl::postBuild(); -} - -bool LLAccordionCtrlTab::notifyChildren (const LLSD& info) -{ - if (info.has("action")) - { - std::string str_action = info["action"]; - if (str_action == "store_state") - { - storeOpenCloseState(); - return true; - } - - if (str_action == "restore_state") - { - restoreOpenCloseState(); - return true; - } - } - - return LLUICtrl::notifyChildren(info); -} - -S32 LLAccordionCtrlTab::notifyParent(const LLSD& info) -{ - if (info.has("action")) - { - std::string str_action = info["action"]; - if (str_action == "size_changes") - { - S32 height = info["height"]; - height = llmax(height, 10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom(); - - mExpandedHeight = height; - - if (isExpanded() && !mSkipChangesOnNotifyParent) - { - LLRect panel_rect = getRect(); - panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height); - reshape(getRect().getWidth(),height); - setRect(panel_rect); - } - - // LLAccordionCtrl should rearrange accordion tab if one of accordions changed its size - if (getParent()) // A parent may not be set if tabs are added dynamically. - getParent()->notifyParent(info); - return 1; - } - - if (str_action == "select_prev") - { - showAndFocusHeader(); - return 1; - } - } - else if (info.has("scrollToShowRect")) - { - LLAccordionCtrl* parent = dynamic_cast(getParent()); - if (parent && parent->getFitParent()) - { - // EXT-8285 ('No attachments worn' text appears at the bottom of blank 'Attachments' accordion) - // The problem was in passing message "scrollToShowRect" IN LLAccordionCtrlTab::notifyParent - // FROM child LLScrollContainer TO parent LLAccordionCtrl with "it_parent" set to true. - - // It is wrong notification for parent accordion which leads to recursive call of adjustContainerPanel - // As the result of recursive call of adjustContainerPanel we got LLAccordionCtrlTab - // that reshaped and re-sized with different rectangles. - - // LLAccordionCtrl has own scrollContainer and LLAccordionCtrlTab has own scrollContainer - // both should handle own scroll container's event. - // So, if parent accordion "fit_parent" accordion tab should handle its scroll container events itself. - - return 1; - } - - if (!getDisplayChildren()) - { - // Don't pass scrolling event further if our contents are invisible (STORM-298). - return 1; - } - } - - return LLUICtrl::notifyParent(info); -} - -S32 LLAccordionCtrlTab::notify(const LLSD& info) -{ - if (info.has("action")) - { - std::string str_action = info["action"]; - if (str_action == "select_first") - { - showAndFocusHeader(); - return 1; - } - - if (str_action == "select_last") - { - if (!getDisplayChildren()) - { - showAndFocusHeader(); - } - else - { - LLView* view = getAccordionView(); - if (view) - { - view->notify(LLSD().with("action", "select_last")); - } - } - } - } - - return 0; -} - -bool LLAccordionCtrlTab::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - if (!mHeader->hasFocus()) - return LLUICtrl::handleKey(key, mask, called_from_parent); - - if ((key == KEY_RETURN) && mask == MASK_NONE) - { - changeOpenClose(getDisplayChildren()); - return true; - } - - if ((key == KEY_ADD || key == KEY_RIGHT) && mask == MASK_NONE) - { - if (!getDisplayChildren()) - { - changeOpenClose(getDisplayChildren()); - return true; - } - } - - if ((key == KEY_SUBTRACT || key == KEY_LEFT) && mask == MASK_NONE) - { - if (getDisplayChildren()) - { - changeOpenClose(getDisplayChildren()); - return true; - } - } - - if (key == KEY_DOWN && mask == MASK_NONE) - { - // if collapsed go to the next accordion - if (!getDisplayChildren()) - { - // we're processing notifyParent so let call parent directly - getParent()->notifyParent(LLSD().with("action", "select_next")); - } - else - { - getAccordionView()->notify(LLSD().with("action", "select_first")); - } - return true; - } - - if (key == KEY_UP && mask == MASK_NONE) - { - // go to the previous accordion - - // we're processing notifyParent so let call parent directly - getParent()->notifyParent(LLSD().with("action", "select_prev")); - return true; - } - - return LLUICtrl::handleKey(key, mask, called_from_parent); -} - -void LLAccordionCtrlTab::showAndFocusHeader() -{ - if (!mHeader) - { - return; - } - - mHeader->setFocus(true); - mHeader->setSelected(mSelectionEnabled); - - LLRect screen_rc; - LLRect selected_rc = mHeader->getRect(); - localRectToScreen(selected_rc, &screen_rc); - - // This call to notifyParent() is intended to deliver "scrollToShowRect" command - // to the parent LLAccordionCtrl so by calling it from the direct parent of this - // accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain - // is shortened and messages from inside the collapsed tabs are avoided. - // See STORM-536. - getParent()->notifyParent(LLSD().with("scrollToShowRect", screen_rc.getValue())); -} - -void LLAccordionCtrlTab::storeOpenCloseState() -{ - if (mWasStateStored) - return; - mStoredOpenCloseState = getDisplayChildren(); - mWasStateStored = true; -} - -void LLAccordionCtrlTab::restoreOpenCloseState() -{ - if (!mWasStateStored) - return; - if (getDisplayChildren() != mStoredOpenCloseState) - { - changeOpenClose(getDisplayChildren()); - } - mWasStateStored = false; -} - -void LLAccordionCtrlTab::adjustContainerPanel() -{ - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); - - LLRect child_rect; - child_rect.setLeftTopAndSize( - getPaddingLeft(), - height - getHeaderHeight() - getPaddingTop(), - width - getPaddingLeft() - getPaddingRight(), - height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); - - adjustContainerPanel(child_rect); -} - -void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect) -{ - if (!mContainerPanel) - return; - - if (!mFitPanel) - { - show_hide_scrollbar(child_rect); - updateLayout(child_rect); - } - else - { - mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight()); - mContainerPanel->setRect(child_rect); - } -} - -S32 LLAccordionCtrlTab::getChildViewHeight() -{ - if (!mContainerPanel) - return 0; - return mContainerPanel->getRect().getHeight(); -} - -void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect) -{ - if (getChildViewHeight() > child_rect.getHeight()) - showScrollbar(child_rect); - else - hideScrollbar(child_rect); -} - -void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect) -{ - if (!mContainerPanel || !mScrollbar) - return; - bool was_visible = mScrollbar->getVisible(); - mScrollbar->setVisible(true); - - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - ctrlSetLeftTopAndSize(mScrollbar, - child_rect.getWidth() - scrollbar_size, - child_rect.getHeight() - PARENT_BORDER_MARGIN, - scrollbar_size, - child_rect.getHeight() - PARENT_BORDER_MARGIN * 2); - - LLRect orig_rect = mContainerPanel->getRect(); - - mScrollbar->setPageSize(child_rect.getHeight()); - mScrollbar->setDocParams(orig_rect.getHeight(), mScrollbar->getDocPos()); - - if (was_visible) - { - S32 scroll_pos = llmin(mScrollbar->getDocPos(), orig_rect.getHeight() - child_rect.getHeight() - 1); - mScrollbar->setDocPos(scroll_pos); - } - else // Shrink child panel - { - updateLayout(child_rect); - } -} - -void LLAccordionCtrlTab::hideScrollbar(const LLRect& child_rect) -{ - if (!mContainerPanel || !mScrollbar) - return; - - if (!mScrollbar->getVisible()) - return; - - mScrollbar->setVisible(false); - mScrollbar->setDocPos(0); - - //shrink child panel - updateLayout(child_rect); -} - -void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*) -{ - LLRect child_rect; - - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); - - child_rect.setLeftTopAndSize( - getPaddingLeft(), - height - getHeaderHeight() - getPaddingTop(), - width - getPaddingLeft() - getPaddingRight(), - height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); - - updateLayout(child_rect); -} - -void LLAccordionCtrlTab::drawChild(const LLRect& root_rect, LLView* child) -{ - if (child && child->getVisible() && child->getRect().isValid()) - { - LLRect screen_rect; - localRectToScreen(child->getRect(), &screen_rect); - - if (root_rect.overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect)) - { - gGL.matrixMode(LLRender::MM_MODELVIEW); - LLUI::pushMatrix(); - { - LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom); - child->draw(); - } - LLUI::popMatrix(); - } - } -} - -void LLAccordionCtrlTab::draw() -{ - if (mFitPanel) - { - LLUICtrl::draw(); - } - else - { - LLRect root_rect(getRootView()->getRect()); - drawChild(root_rect, mHeader); - drawChild(root_rect, mScrollbar); - - LLRect child_rect; - - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); - - child_rect.setLeftTopAndSize( - getPaddingLeft(), - height - getHeaderHeight() - getPaddingTop(), - width - getPaddingLeft() - getPaddingRight(), - height - getHeaderHeight() - getPaddingTop() - getPaddingBottom()); - - LLLocalClipRect clip(child_rect); - drawChild(root_rect,mContainerPanel); - } -} - -void LLAccordionCtrlTab::updateLayout(const LLRect& child_rect) -{ - LLView* child = getAccordionView(); - if (!mContainerPanel) - return; - - S32 panel_top = child_rect.getHeight(); - S32 panel_width = child_rect.getWidth(); - - static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); - if (mScrollbar && mScrollbar->getVisible()) - { - panel_top += mScrollbar->getDocPos(); - panel_width -= scrollbar_size; - } - - // Set sizes for first panels and dragbars - LLRect panel_rect = child->getRect(); - ctrlSetLeftTopAndSize(mContainerPanel, child_rect.mLeft, panel_top, panel_width, panel_rect.getHeight()); -} - -void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height) -{ - if (!panel) - return; - LLRect panel_rect = panel->getRect(); - panel_rect.setLeftTopAndSize(left, top, width, height); - panel->reshape( width, height, 1); - panel->setRect(panel_rect); -} - -bool LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) -{ - //header may be not the first child but we need to process it first - if (y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT / 2)) - { - //inside tab header - //fix for EXT-6619 - mHeader->handleToolTip(x, y, mask); - return true; - } - return LLUICtrl::handleToolTip(x, y, mask); -} - -bool LLAccordionCtrlTab::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if (LLUICtrl::handleScrollWheel(x, y, clicks)) - { - return true; - } - - if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) - { - return true; - } - - return false; -} +/** + * @file LLAccordionCtrlTab.cpp + * @brief Collapsible control implementation + * + * $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 "llaccordionctrltab.h" +#include "llaccordionctrl.h" + +#include "lllocalcliprect.h" +#include "llscrollbar.h" +#include "lltextbox.h" +#include "lltextutil.h" +#include "lluictrl.h" + +static const std::string DD_BUTTON_NAME = "dd_button"; +static const std::string DD_TEXTBOX_NAME = "dd_textbox"; +static const std::string DD_HEADER_NAME = "dd_header"; + +static const S32 HEADER_HEIGHT = 23; +static const S32 HEADER_IMAGE_LEFT_OFFSET = 5; +static const S32 HEADER_TEXT_LEFT_OFFSET = 30; +static const F32 AUTO_OPEN_TIME = 1.f; +static const S32 VERTICAL_MULTIPLE = 16; +static const S32 PARENT_BORDER_MARGIN = 5; + +static LLDefaultChildRegistry::Register t1("accordion_tab"); + +class LLAccordionCtrlTab::LLAccordionCtrlTabHeader : public LLUICtrl +{ +public: + friend class LLUICtrlFactory; + + struct Params : public LLInitParam::Block + { + Params(); + }; + + LLAccordionCtrlTabHeader(const LLAccordionCtrlTabHeader::Params& p); + + virtual ~LLAccordionCtrlTabHeader(); + + virtual void draw(); + + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + + virtual bool postBuild(); + + std::string getTitle(); + void setTitle(const std::string& title, const std::string& hl); + + void setTitleFontStyle(std::string style); + + void setTitleColor(LLUIColor); + + void setSelected(bool is_selected) { mIsSelected = is_selected; } + + virtual void onMouseEnter(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask); + virtual bool handleKey(KEY key, MASK mask, 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); + +private: + LLTextBox* mHeaderTextbox; + + // Overlay images (arrows) + LLPointer mImageCollapsed; + LLPointer mImageExpanded; + LLPointer mImageCollapsedPressed; + LLPointer mImageExpandedPressed; + + // Background images + LLPointer mImageHeader; + LLPointer mImageHeaderOver; + LLPointer mImageHeaderPressed; + LLPointer mImageHeaderFocused; + + // style saved when applying it in setTitleFontStyle + LLStyle::Params mStyleParams; + + LLUIColor mHeaderBGColor; + + bool mNeedsHighlight; + bool mIsSelected; + + LLFrameTimer mAutoOpenTimer; +}; + +LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params() +{ +} + +LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader( + const LLAccordionCtrlTabHeader::Params& p) +: LLUICtrl(p) +, mHeaderBGColor(p.header_bg_color()) +, mNeedsHighlight(false) +, mIsSelected(false), + mImageCollapsed(p.header_collapse_img), + mImageCollapsedPressed(p.header_collapse_img_pressed), + mImageExpanded(p.header_expand_img), + mImageExpandedPressed(p.header_expand_img_pressed), + mImageHeader(p.header_image), + mImageHeaderOver(p.header_image_over), + mImageHeaderPressed(p.header_image_pressed), + mImageHeaderFocused(p.header_image_focused) +{ + LLTextBox::Params textboxParams; + textboxParams.name(DD_TEXTBOX_NAME); + textboxParams.initial_value(p.title()); + textboxParams.text_color(p.header_text_color()); + textboxParams.follows.flags(FOLLOWS_NONE); + textboxParams.font( p.font() ); + textboxParams.font_shadow(LLFontGL::NO_SHADOW); + textboxParams.use_ellipses = true; + textboxParams.bg_visible = false; + textboxParams.mouse_opaque = false; + textboxParams.parse_urls = false; + mHeaderTextbox = LLUICtrlFactory::create(textboxParams); + addChild(mHeaderTextbox); +} + +LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader() +{ +} + +bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild() +{ + return true; +} + +std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle() +{ + if (mHeaderTextbox) + { + return mHeaderTextbox->getText(); + } + + return LLStringUtil::null; +} + +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl) +{ + if (mHeaderTextbox) + { + LLTextUtil::textboxSetHighlightedVal( + mHeaderTextbox, + mStyleParams, + title, + hl); + } +} + +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string style) +{ + if (mHeaderTextbox) + { + std::string text = mHeaderTextbox->getText(); + mStyleParams.font(mHeaderTextbox->getFont()); + mStyleParams.font.style(style); + mHeaderTextbox->setText(text, mStyleParams); + } +} + +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleColor(LLUIColor color) +{ + if (mHeaderTextbox) + { + mHeaderTextbox->setColor(color); + } +} + +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() +{ + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); + + F32 alpha = getCurrentTransparency(); + gl_rect_2d(0, 0, width - 1, height - 1, mHeaderBGColor.get() % alpha, true); + + LLAccordionCtrlTab* parent = dynamic_cast(getParent()); + bool collapsible = parent && parent->getCollapsible(); + bool expanded = parent && parent->getDisplayChildren(); + + // Handle overlay images, if needed + // Only show green "focus" background image if the accordion is open, + // because the user's mental model of focus is that it goes away after + // the accordion is closed. + if (getParent()->hasFocus() || mIsSelected + /*&& !(collapsible && !expanded)*/ // WHY?? + ) + { + mImageHeaderFocused->draw(0, 0, width, height); + } + else + { + mImageHeader->draw(0, 0, width, height); + } + + if (mNeedsHighlight) + { + mImageHeaderOver->draw(0, 0, width, height); + } + + if (collapsible) + { + LLPointer overlay_image; + if (expanded) + { + overlay_image = mImageExpanded; + } + else + { + overlay_image = mImageCollapsed; + } + overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET, (height - overlay_image->getHeight()) / 2); + } + + LLUICtrl::draw(); +} + +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */) +{ + S32 header_height = mHeaderTextbox->getTextPixelHeight(); + + LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2); + mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight()); + mHeaderTextbox->setRect(textboxRect); + + if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth()) + { + setToolTip(mHeaderTextbox->getText()); + } + else + { + setToolTip(LLStringUtil::null); + } +} + +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MASK mask) +{ + LLUICtrl::onMouseEnter(x, y, mask); + mNeedsHighlight = true; +} + +void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask) +{ + LLUICtrl::onMouseLeave(x, y, mask); + mNeedsHighlight = false; + mAutoOpenTimer.stop(); +} + +bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, bool called_from_parent) +{ + if ((key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE) + { + return getParent()->handleKey(key, mask, called_from_parent); + } + + return LLUICtrl::handleKey(key, mask, called_from_parent); +} + +bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + LLAccordionCtrlTab* parent = dynamic_cast(getParent()); + + if (parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose()) + { + if (mAutoOpenTimer.getStarted()) + { + if (mAutoOpenTimer.getElapsedTimeF32() > AUTO_OPEN_TIME) + { + parent->changeOpenClose(false); + mAutoOpenTimer.stop(); + return true; + } + } + else + { + mAutoOpenTimer.start(); + } + } + + return LLUICtrl::handleDragAndDrop(x, y, mask, drop, cargo_type, + cargo_data, accept, tooltip_msg); +} + +LLAccordionCtrlTab::Params::Params() + : title("title") + ,display_children("expanded", true) + ,header_height("header_height", HEADER_HEIGHT), + min_width("min_width", 0), + min_height("min_height", 0) + ,collapsible("collapsible", true) + ,header_bg_color("header_bg_color") + ,dropdown_bg_color("dropdown_bg_color") + ,header_visible("header_visible",true) + ,padding_left("padding_left",2) + ,padding_right("padding_right",2) + ,padding_top("padding_top",2) + ,padding_bottom("padding_bottom",2) + ,header_expand_img("header_expand_img") + ,header_expand_img_pressed("header_expand_img_pressed") + ,header_collapse_img("header_collapse_img") + ,header_collapse_img_pressed("header_collapse_img_pressed") + ,header_image("header_image") + ,header_image_over("header_image_over") + ,header_image_pressed("header_image_pressed") + ,header_image_focused("header_image_focused") + ,header_text_color("header_text_color") + ,fit_panel("fit_panel",true) + ,selection_enabled("selection_enabled", false) +{ + changeDefault(mouse_opaque, false); +} + +LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p) + : LLUICtrl(p) + ,mDisplayChildren(p.display_children) + ,mCollapsible(p.collapsible) + ,mExpandedHeight(0) + ,mDropdownBGColor(p.dropdown_bg_color()) + ,mHeaderVisible(p.header_visible) + ,mPaddingLeft(p.padding_left) + ,mPaddingRight(p.padding_right) + ,mPaddingTop(p.padding_top) + ,mPaddingBottom(p.padding_bottom) + ,mCanOpenClose(true) + ,mFitPanel(p.fit_panel) + ,mSelectionEnabled(p.selection_enabled) + ,mContainerPanel(NULL) + ,mScrollbar(NULL) +{ + mStoredOpenCloseState = false; + mWasStateStored = false; + mSkipChangesOnNotifyParent = false; + + mDropdownBGColor = LLColor4::white; + LLAccordionCtrlTabHeader::Params headerParams; + headerParams.name(DD_HEADER_NAME); + headerParams.title(p.title); + mHeader = LLUICtrlFactory::create(headerParams); + addChild(mHeader, 1); + + LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLAccordionCtrlTab::selectOnFocusReceived, this)); + + if (!p.selection_enabled) + { + LLFocusableElement::setFocusLostCallback(boost::bind(&LLAccordionCtrlTab::deselectOnFocusLost, this)); + } + + reshape(100, 200,false); +} + +LLAccordionCtrlTab::~LLAccordionCtrlTab() +{ +} + +void LLAccordionCtrlTab::setDisplayChildren(bool display) +{ + mDisplayChildren = display; + LLRect rect = getRect(); + + rect.mBottom = rect.mTop - (getDisplayChildren() ? mExpandedHeight : HEADER_HEIGHT); + setRect(rect); + + if (mContainerPanel) + { + mContainerPanel->setVisible(getDisplayChildren()); + } + + if (mDisplayChildren) + { + adjustContainerPanel(); + } + else + { + if (mScrollbar) + mScrollbar->setVisible(false); + } +} + +void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent /* = true */) +{ + LLRect headerRect; + + headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT); + mHeader->setRect(headerRect); + mHeader->reshape(headerRect.getWidth(), headerRect.getHeight()); + + if (!mDisplayChildren) + return; + + LLRect childRect; + + childRect.setLeftTopAndSize( + getPaddingLeft(), + height - getHeaderHeight() - getPaddingTop(), + width - getPaddingLeft() - getPaddingRight(), + height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); + + adjustContainerPanel(childRect); +} + +void LLAccordionCtrlTab::changeOpenClose(bool is_open) +{ + if (is_open) + mExpandedHeight = getRect().getHeight(); + + setDisplayChildren(!is_open); + reshape(getRect().getWidth(), getRect().getHeight(), false); + if (mCommitSignal) + { + (*mCommitSignal)(this, getDisplayChildren()); + } +} + +void LLAccordionCtrlTab::onVisibilityChange(bool new_visibility) +{ + LLUICtrl::onVisibilityChange(new_visibility); + + notifyParent(LLSD().with("child_visibility_change", new_visibility)); +} + +// virtual +void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl) +{ + if (mScrollbar && mScrollbar->getVisible()) + { + LLRect rect; + cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this); + + // Translate to parent coordinatess to check if we are in visible rectangle + rect.translate(getRect().mLeft, getRect().mBottom); + + if (!getRect().contains(rect)) + { + // for accordition's scroll, height is in pixels + // Back to local coords and calculate position for scroller + S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom; + S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop; + + S32 scroll_pos = llclamp(mScrollbar->getDocPos(), + bottom, // min vertical scroll + top); // max vertical scroll + + mScrollbar->setDocPos(scroll_pos); + } + } + + LLUICtrl::onUpdateScrollToChild(cntrl); +} + +bool LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (mCollapsible && mHeaderVisible && mCanOpenClose) + { + if (y >= (getRect().getHeight() - HEADER_HEIGHT)) + { + mHeader->setFocus(true); + changeOpenClose(getDisplayChildren()); + + // Reset stored state + mWasStateStored = false; + return true; + } + } + return LLUICtrl::handleMouseDown(x,y,mask); +} + +bool LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask) +{ + return LLUICtrl::handleMouseUp(x,y,mask); +} + +boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback(commit_callback_t cb) +{ + return setCommitCallback(cb); +} + +bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group) +{ + if (DD_HEADER_NAME != child->getName()) + { + reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT ); + mExpandedHeight = getRect().getHeight(); + } + + bool res = LLUICtrl::addChild(child, tab_group); + + if (DD_HEADER_NAME != child->getName()) + { + if (!mCollapsible) + setDisplayChildren(true); + else + setDisplayChildren(getDisplayChildren()); + } + + if (!mContainerPanel) + mContainerPanel = findContainerView(); + + return res; +} + +void LLAccordionCtrlTab::setAccordionView(LLView* panel) +{ + addChild(panel, 0); +} + +std::string LLAccordionCtrlTab::getTitle() const +{ + if (mHeader) + { + return mHeader->getTitle(); + } + + return LLStringUtil::null; +} + +void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl) +{ + if (mHeader) + { + mHeader->setTitle(title, hl); + } +} + +void LLAccordionCtrlTab::setTitleFontStyle(std::string style) +{ + if (mHeader) + { + mHeader->setTitleFontStyle(style); + } +} + +void LLAccordionCtrlTab::setTitleColor(LLUIColor color) +{ + if (mHeader) + { + mHeader->setTitleColor(color); + } +} + +boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb) +{ + if (mHeader) + { + return mHeader->setFocusReceivedCallback(cb); + } + + return boost::signals2::connection(); +} + +boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb) +{ + if (mHeader) + { + return mHeader->setFocusLostCallback(cb); + } + + return boost::signals2::connection(); +} + +void LLAccordionCtrlTab::setSelected(bool is_selected) +{ + if (mHeader) + { + mHeader->setSelected(is_selected); + } +} + +LLView* LLAccordionCtrlTab::findContainerView() +{ + child_list_const_iter_t it = getChildList()->begin(), it_end = getChildList()->end(); + while (it != it_end) + { + LLView* child = *(it++); + if (DD_HEADER_NAME != child->getName() && child->getVisible()) + return child; + } + + return NULL; +} + +void LLAccordionCtrlTab::selectOnFocusReceived() +{ + if (getParent()) // A parent may not be set if tabs are added dynamically. + { + getParent()->notifyParent(LLSD().with("action", "select_current")); + } +} + +void LLAccordionCtrlTab::deselectOnFocusLost() +{ + if (getParent()) // A parent may not be set if tabs are added dynamically. + { + getParent()->notifyParent(LLSD().with("action", "deselect_current")); + } +} + +S32 LLAccordionCtrlTab::getHeaderHeight() +{ + return mHeaderVisible ? HEADER_HEIGHT : 0; +} + +void LLAccordionCtrlTab::setHeaderVisible(bool value) +{ + if (mHeaderVisible == value) + return; + + mHeaderVisible = value; + + if (mHeader) + { + mHeader->setVisible(value); + } + + reshape(getRect().getWidth(), getRect().getHeight(), false); +}; + +//virtual +bool LLAccordionCtrlTab::postBuild() +{ + if (mHeader) + { + mHeader->setVisible(mHeaderVisible); + } + + static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); + + LLRect scroll_rect; + scroll_rect.setOriginAndSize( + getRect().getWidth() - scrollbar_size, + 1, + scrollbar_size, + getRect().getHeight() - 1); + + mContainerPanel = findContainerView(); + + if (!mFitPanel) + { + LLScrollbar::Params sbparams; + sbparams.name("scrollable vertical"); + sbparams.rect(scroll_rect); + sbparams.orientation(LLScrollbar::VERTICAL); + sbparams.doc_size(getRect().getHeight()); + sbparams.doc_pos(0); + sbparams.page_size(getRect().getHeight()); + sbparams.step_size(VERTICAL_MULTIPLE); + sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + sbparams.change_callback(boost::bind(&LLAccordionCtrlTab::onScrollPosChangeCallback, this, _1, _2)); + + mScrollbar = LLUICtrlFactory::create(sbparams); + LLView::addChild(mScrollbar); + mScrollbar->setFollowsRight(); + mScrollbar->setFollowsTop(); + mScrollbar->setFollowsBottom(); + + mScrollbar->setVisible(false); + } + + if (mContainerPanel) + { + mContainerPanel->setVisible(mDisplayChildren); + } + + return LLUICtrl::postBuild(); +} + +bool LLAccordionCtrlTab::notifyChildren (const LLSD& info) +{ + if (info.has("action")) + { + std::string str_action = info["action"]; + if (str_action == "store_state") + { + storeOpenCloseState(); + return true; + } + + if (str_action == "restore_state") + { + restoreOpenCloseState(); + return true; + } + } + + return LLUICtrl::notifyChildren(info); +} + +S32 LLAccordionCtrlTab::notifyParent(const LLSD& info) +{ + if (info.has("action")) + { + std::string str_action = info["action"]; + if (str_action == "size_changes") + { + S32 height = info["height"]; + height = llmax(height, 10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom(); + + mExpandedHeight = height; + + if (isExpanded() && !mSkipChangesOnNotifyParent) + { + LLRect panel_rect = getRect(); + panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height); + reshape(getRect().getWidth(),height); + setRect(panel_rect); + } + + // LLAccordionCtrl should rearrange accordion tab if one of accordions changed its size + if (getParent()) // A parent may not be set if tabs are added dynamically. + getParent()->notifyParent(info); + return 1; + } + + if (str_action == "select_prev") + { + showAndFocusHeader(); + return 1; + } + } + else if (info.has("scrollToShowRect")) + { + LLAccordionCtrl* parent = dynamic_cast(getParent()); + if (parent && parent->getFitParent()) + { + // EXT-8285 ('No attachments worn' text appears at the bottom of blank 'Attachments' accordion) + // The problem was in passing message "scrollToShowRect" IN LLAccordionCtrlTab::notifyParent + // FROM child LLScrollContainer TO parent LLAccordionCtrl with "it_parent" set to true. + + // It is wrong notification for parent accordion which leads to recursive call of adjustContainerPanel + // As the result of recursive call of adjustContainerPanel we got LLAccordionCtrlTab + // that reshaped and re-sized with different rectangles. + + // LLAccordionCtrl has own scrollContainer and LLAccordionCtrlTab has own scrollContainer + // both should handle own scroll container's event. + // So, if parent accordion "fit_parent" accordion tab should handle its scroll container events itself. + + return 1; + } + + if (!getDisplayChildren()) + { + // Don't pass scrolling event further if our contents are invisible (STORM-298). + return 1; + } + } + + return LLUICtrl::notifyParent(info); +} + +S32 LLAccordionCtrlTab::notify(const LLSD& info) +{ + if (info.has("action")) + { + std::string str_action = info["action"]; + if (str_action == "select_first") + { + showAndFocusHeader(); + return 1; + } + + if (str_action == "select_last") + { + if (!getDisplayChildren()) + { + showAndFocusHeader(); + } + else + { + LLView* view = getAccordionView(); + if (view) + { + view->notify(LLSD().with("action", "select_last")); + } + } + } + } + + return 0; +} + +bool LLAccordionCtrlTab::handleKey(KEY key, MASK mask, bool called_from_parent) +{ + if (!mHeader->hasFocus()) + return LLUICtrl::handleKey(key, mask, called_from_parent); + + if ((key == KEY_RETURN) && mask == MASK_NONE) + { + changeOpenClose(getDisplayChildren()); + return true; + } + + if ((key == KEY_ADD || key == KEY_RIGHT) && mask == MASK_NONE) + { + if (!getDisplayChildren()) + { + changeOpenClose(getDisplayChildren()); + return true; + } + } + + if ((key == KEY_SUBTRACT || key == KEY_LEFT) && mask == MASK_NONE) + { + if (getDisplayChildren()) + { + changeOpenClose(getDisplayChildren()); + return true; + } + } + + if (key == KEY_DOWN && mask == MASK_NONE) + { + // if collapsed go to the next accordion + if (!getDisplayChildren()) + { + // we're processing notifyParent so let call parent directly + getParent()->notifyParent(LLSD().with("action", "select_next")); + } + else + { + getAccordionView()->notify(LLSD().with("action", "select_first")); + } + return true; + } + + if (key == KEY_UP && mask == MASK_NONE) + { + // go to the previous accordion + + // we're processing notifyParent so let call parent directly + getParent()->notifyParent(LLSD().with("action", "select_prev")); + return true; + } + + return LLUICtrl::handleKey(key, mask, called_from_parent); +} + +void LLAccordionCtrlTab::showAndFocusHeader() +{ + if (!mHeader) + { + return; + } + + mHeader->setFocus(true); + mHeader->setSelected(mSelectionEnabled); + + LLRect screen_rc; + LLRect selected_rc = mHeader->getRect(); + localRectToScreen(selected_rc, &screen_rc); + + // This call to notifyParent() is intended to deliver "scrollToShowRect" command + // to the parent LLAccordionCtrl so by calling it from the direct parent of this + // accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain + // is shortened and messages from inside the collapsed tabs are avoided. + // See STORM-536. + getParent()->notifyParent(LLSD().with("scrollToShowRect", screen_rc.getValue())); +} + +void LLAccordionCtrlTab::storeOpenCloseState() +{ + if (mWasStateStored) + return; + mStoredOpenCloseState = getDisplayChildren(); + mWasStateStored = true; +} + +void LLAccordionCtrlTab::restoreOpenCloseState() +{ + if (!mWasStateStored) + return; + if (getDisplayChildren() != mStoredOpenCloseState) + { + changeOpenClose(getDisplayChildren()); + } + mWasStateStored = false; +} + +void LLAccordionCtrlTab::adjustContainerPanel() +{ + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); + + LLRect child_rect; + child_rect.setLeftTopAndSize( + getPaddingLeft(), + height - getHeaderHeight() - getPaddingTop(), + width - getPaddingLeft() - getPaddingRight(), + height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); + + adjustContainerPanel(child_rect); +} + +void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect) +{ + if (!mContainerPanel) + return; + + if (!mFitPanel) + { + show_hide_scrollbar(child_rect); + updateLayout(child_rect); + } + else + { + mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight()); + mContainerPanel->setRect(child_rect); + } +} + +S32 LLAccordionCtrlTab::getChildViewHeight() +{ + if (!mContainerPanel) + return 0; + return mContainerPanel->getRect().getHeight(); +} + +void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect) +{ + if (getChildViewHeight() > child_rect.getHeight()) + showScrollbar(child_rect); + else + hideScrollbar(child_rect); +} + +void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect) +{ + if (!mContainerPanel || !mScrollbar) + return; + bool was_visible = mScrollbar->getVisible(); + mScrollbar->setVisible(true); + + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + ctrlSetLeftTopAndSize(mScrollbar, + child_rect.getWidth() - scrollbar_size, + child_rect.getHeight() - PARENT_BORDER_MARGIN, + scrollbar_size, + child_rect.getHeight() - PARENT_BORDER_MARGIN * 2); + + LLRect orig_rect = mContainerPanel->getRect(); + + mScrollbar->setPageSize(child_rect.getHeight()); + mScrollbar->setDocParams(orig_rect.getHeight(), mScrollbar->getDocPos()); + + if (was_visible) + { + S32 scroll_pos = llmin(mScrollbar->getDocPos(), orig_rect.getHeight() - child_rect.getHeight() - 1); + mScrollbar->setDocPos(scroll_pos); + } + else // Shrink child panel + { + updateLayout(child_rect); + } +} + +void LLAccordionCtrlTab::hideScrollbar(const LLRect& child_rect) +{ + if (!mContainerPanel || !mScrollbar) + return; + + if (!mScrollbar->getVisible()) + return; + + mScrollbar->setVisible(false); + mScrollbar->setDocPos(0); + + //shrink child panel + updateLayout(child_rect); +} + +void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*) +{ + LLRect child_rect; + + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); + + child_rect.setLeftTopAndSize( + getPaddingLeft(), + height - getHeaderHeight() - getPaddingTop(), + width - getPaddingLeft() - getPaddingRight(), + height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); + + updateLayout(child_rect); +} + +void LLAccordionCtrlTab::drawChild(const LLRect& root_rect, LLView* child) +{ + if (child && child->getVisible() && child->getRect().isValid()) + { + LLRect screen_rect; + localRectToScreen(child->getRect(), &screen_rect); + + if (root_rect.overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect)) + { + gGL.matrixMode(LLRender::MM_MODELVIEW); + LLUI::pushMatrix(); + { + LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom); + child->draw(); + } + LLUI::popMatrix(); + } + } +} + +void LLAccordionCtrlTab::draw() +{ + if (mFitPanel) + { + LLUICtrl::draw(); + } + else + { + LLRect root_rect(getRootView()->getRect()); + drawChild(root_rect, mHeader); + drawChild(root_rect, mScrollbar); + + LLRect child_rect; + + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); + + child_rect.setLeftTopAndSize( + getPaddingLeft(), + height - getHeaderHeight() - getPaddingTop(), + width - getPaddingLeft() - getPaddingRight(), + height - getHeaderHeight() - getPaddingTop() - getPaddingBottom()); + + LLLocalClipRect clip(child_rect); + drawChild(root_rect,mContainerPanel); + } +} + +void LLAccordionCtrlTab::updateLayout(const LLRect& child_rect) +{ + LLView* child = getAccordionView(); + if (!mContainerPanel) + return; + + S32 panel_top = child_rect.getHeight(); + S32 panel_width = child_rect.getWidth(); + + static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); + if (mScrollbar && mScrollbar->getVisible()) + { + panel_top += mScrollbar->getDocPos(); + panel_width -= scrollbar_size; + } + + // Set sizes for first panels and dragbars + LLRect panel_rect = child->getRect(); + ctrlSetLeftTopAndSize(mContainerPanel, child_rect.mLeft, panel_top, panel_width, panel_rect.getHeight()); +} + +void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height) +{ + if (!panel) + return; + LLRect panel_rect = panel->getRect(); + panel_rect.setLeftTopAndSize(left, top, width, height); + panel->reshape( width, height, 1); + panel->setRect(panel_rect); +} + +bool LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) +{ + //header may be not the first child but we need to process it first + if (y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT / 2)) + { + //inside tab header + //fix for EXT-6619 + mHeader->handleToolTip(x, y, mask); + return true; + } + return LLUICtrl::handleToolTip(x, y, mask); +} + +bool LLAccordionCtrlTab::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if (LLUICtrl::handleScrollWheel(x, y, clicks)) + { + return true; + } + + if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) + { + return true; + } + + return false; +} diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index b03ea53609..cf3569683e 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -1,251 +1,251 @@ -/** - * @file LLAccordionCtrlTab.h - * @brief Collapsible box control implementation - * - * $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_ACCORDIONCTRLTAB_H_ -#define LL_ACCORDIONCTRLTAB_H_ - -#include -#include "llrect.h" -#include "lluictrl.h" -#include "lluicolor.h" -#include "llstyle.h" - -class LLUICtrlFactory; -class LLUIImage; -class LLButton; -class LLTextBox; -class LLScrollbar; - - - -// LLAccordionCtrlTab is a container for other controls. -// It has a Header, by clicking on which hosted controls are shown or hidden. -// When hosted controls are show - LLAccordionCtrlTab is expanded. -// When hosted controls are hidden - LLAccordionCtrlTab is collapsed. - -class LLAccordionCtrlTab : public LLUICtrl -{ -// Interface -public: - - struct Params - : public LLInitParam::Block - { - Optional display_children, //expanded or collapsed after initialization - collapsible; - - Optional title; - - Optional header_height, - min_width, - min_height; - - // Overlay images (arrows on the left) - Mandatory header_expand_img, - header_expand_img_pressed, - header_collapse_img, - header_collapse_img_pressed; - - // Background images for the accordion tabs - Mandatory header_image, - header_image_over, - header_image_pressed, - header_image_focused; - - Optional header_bg_color, - header_text_color, - dropdown_bg_color; - - Optional header_visible; - - Optional fit_panel; - - Optional selection_enabled; - - Optional padding_left, - padding_right, - padding_top, - padding_bottom; - - Params(); - }; - - typedef LLDefaultChildRegistry child_registry_t; - - virtual ~LLAccordionCtrlTab(); - - // Registers callback for expand/collapse events. - boost::signals2::connection setDropDownStateChangedCallback(commit_callback_t cb); - - // Changes expand/collapse state - virtual void setDisplayChildren(bool display); - - // Returns expand/collapse state - virtual bool getDisplayChildren() const { return mDisplayChildren; }; - - //set LLAccordionCtrlTab panel - void setAccordionView(LLView* panel); - LLView* getAccordionView() { return mContainerPanel; }; - - std::string getTitle() const; - - // Set text and highlight substring in LLAccordionCtrlTabHeader - void setTitle(const std::string& title, const std::string& hl = LLStringUtil::null); - - // Set text font style in LLAccordionCtrlTabHeader - void setTitleFontStyle(std::string style); - - // Set text color in LLAccordionCtrlTabHeader - void setTitleColor(LLUIColor color); - - boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb); - boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb); - - void setSelected(bool is_selected); - - bool getCollapsible() { return mCollapsible; }; - - void setCollapsible(bool collapsible) { mCollapsible = collapsible; }; - void changeOpenClose(bool is_open); - - void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close; }; - bool canOpenClose() const { return mCanOpenClose; }; - - virtual bool postBuild(); - - S32 notifyParent(const LLSD& info); - S32 notify(const LLSD& info); - bool notifyChildren(const LLSD& info); - - void draw(); - - void storeOpenCloseState(); - void restoreOpenCloseState(); - -protected: - LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&); - friend class LLUICtrlFactory; - -// Overrides -public: - - // Call reshape after changing size - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - - /** - * Raises notifyParent event with "child_visibility_change" = new_visibility - */ - void onVisibilityChange(bool new_visibility); - virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); - - // Changes expand/collapse state and triggers expand/collapse callbacks - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); - - virtual bool handleToolTip(S32 x, S32 y, MASK mask); - virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); - - - virtual bool addChild(LLView* child, S32 tab_group = 0 ); - - bool isExpanded() const { return mDisplayChildren; } - - S32 getHeaderHeight(); - - // Min size functions - - void setHeaderVisible(bool value); - - bool getHeaderVisible() { return mHeaderVisible;} - - S32 mExpandedHeight; // Height of expanded ctrl. - // Used to restore height after expand. - - S32 getPaddingLeft() const { return mPaddingLeft;} - S32 getPaddingRight() const { return mPaddingRight;} - S32 getPaddingTop() const { return mPaddingTop;} - S32 getPaddingBottom() const { return mPaddingBottom;} - - void showAndFocusHeader(); - - void setFitPanel( bool fit ) { mFitPanel = true; } - bool getFitParent() const { return mFitPanel; } - - void setIgnoreResizeNotification(bool ignore) { mSkipChangesOnNotifyParent = ignore;} - -protected: - void adjustContainerPanel (const LLRect& child_rect); - void adjustContainerPanel (); - S32 getChildViewHeight (); - - void onScrollPosChangeCallback(S32, LLScrollbar*); - - void show_hide_scrollbar (const LLRect& child_rect); - void showScrollbar (const LLRect& child_rect); - void hideScrollbar (const LLRect& child_rect); - - void updateLayout ( const LLRect& child_rect ); - void ctrlSetLeftTopAndSize (LLView* panel, S32 left, S32 top, S32 width, S32 height); - - void drawChild(const LLRect& root_rect,LLView* child); - - LLView* findContainerView (); - - void selectOnFocusReceived(); - void deselectOnFocusLost(); - -private: - - class LLAccordionCtrlTabHeader; - LLAccordionCtrlTabHeader* mHeader; //Header - - bool mDisplayChildren; //Expanded/collapsed - bool mCollapsible; - bool mHeaderVisible; - - bool mCanOpenClose; - bool mFitPanel; - - S32 mPaddingLeft; - S32 mPaddingRight; - S32 mPaddingTop; - S32 mPaddingBottom; - - bool mStoredOpenCloseState; - bool mWasStateStored; - bool mSkipChangesOnNotifyParent; - - bool mSelectionEnabled; - - LLScrollbar* mScrollbar; - LLView* mContainerPanel; - - LLUIColor mDropdownBGColor; -}; - -#endif +/** + * @file LLAccordionCtrlTab.h + * @brief Collapsible box control implementation + * + * $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_ACCORDIONCTRLTAB_H_ +#define LL_ACCORDIONCTRLTAB_H_ + +#include +#include "llrect.h" +#include "lluictrl.h" +#include "lluicolor.h" +#include "llstyle.h" + +class LLUICtrlFactory; +class LLUIImage; +class LLButton; +class LLTextBox; +class LLScrollbar; + + + +// LLAccordionCtrlTab is a container for other controls. +// It has a Header, by clicking on which hosted controls are shown or hidden. +// When hosted controls are show - LLAccordionCtrlTab is expanded. +// When hosted controls are hidden - LLAccordionCtrlTab is collapsed. + +class LLAccordionCtrlTab : public LLUICtrl +{ +// Interface +public: + + struct Params + : public LLInitParam::Block + { + Optional display_children, //expanded or collapsed after initialization + collapsible; + + Optional title; + + Optional header_height, + min_width, + min_height; + + // Overlay images (arrows on the left) + Mandatory header_expand_img, + header_expand_img_pressed, + header_collapse_img, + header_collapse_img_pressed; + + // Background images for the accordion tabs + Mandatory header_image, + header_image_over, + header_image_pressed, + header_image_focused; + + Optional header_bg_color, + header_text_color, + dropdown_bg_color; + + Optional header_visible; + + Optional fit_panel; + + Optional selection_enabled; + + Optional padding_left, + padding_right, + padding_top, + padding_bottom; + + Params(); + }; + + typedef LLDefaultChildRegistry child_registry_t; + + virtual ~LLAccordionCtrlTab(); + + // Registers callback for expand/collapse events. + boost::signals2::connection setDropDownStateChangedCallback(commit_callback_t cb); + + // Changes expand/collapse state + virtual void setDisplayChildren(bool display); + + // Returns expand/collapse state + virtual bool getDisplayChildren() const { return mDisplayChildren; }; + + //set LLAccordionCtrlTab panel + void setAccordionView(LLView* panel); + LLView* getAccordionView() { return mContainerPanel; }; + + std::string getTitle() const; + + // Set text and highlight substring in LLAccordionCtrlTabHeader + void setTitle(const std::string& title, const std::string& hl = LLStringUtil::null); + + // Set text font style in LLAccordionCtrlTabHeader + void setTitleFontStyle(std::string style); + + // Set text color in LLAccordionCtrlTabHeader + void setTitleColor(LLUIColor color); + + boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb); + boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb); + + void setSelected(bool is_selected); + + bool getCollapsible() { return mCollapsible; }; + + void setCollapsible(bool collapsible) { mCollapsible = collapsible; }; + void changeOpenClose(bool is_open); + + void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close; }; + bool canOpenClose() const { return mCanOpenClose; }; + + virtual bool postBuild(); + + S32 notifyParent(const LLSD& info); + S32 notify(const LLSD& info); + bool notifyChildren(const LLSD& info); + + void draw(); + + void storeOpenCloseState(); + void restoreOpenCloseState(); + +protected: + LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&); + friend class LLUICtrlFactory; + +// Overrides +public: + + // Call reshape after changing size + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + + /** + * Raises notifyParent event with "child_visibility_change" = new_visibility + */ + void onVisibilityChange(bool new_visibility); + virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); + + // Changes expand/collapse state and triggers expand/collapse callbacks + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); + + virtual bool handleToolTip(S32 x, S32 y, MASK mask); + virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); + + + virtual bool addChild(LLView* child, S32 tab_group = 0 ); + + bool isExpanded() const { return mDisplayChildren; } + + S32 getHeaderHeight(); + + // Min size functions + + void setHeaderVisible(bool value); + + bool getHeaderVisible() { return mHeaderVisible;} + + S32 mExpandedHeight; // Height of expanded ctrl. + // Used to restore height after expand. + + S32 getPaddingLeft() const { return mPaddingLeft;} + S32 getPaddingRight() const { return mPaddingRight;} + S32 getPaddingTop() const { return mPaddingTop;} + S32 getPaddingBottom() const { return mPaddingBottom;} + + void showAndFocusHeader(); + + void setFitPanel( bool fit ) { mFitPanel = true; } + bool getFitParent() const { return mFitPanel; } + + void setIgnoreResizeNotification(bool ignore) { mSkipChangesOnNotifyParent = ignore;} + +protected: + void adjustContainerPanel (const LLRect& child_rect); + void adjustContainerPanel (); + S32 getChildViewHeight (); + + void onScrollPosChangeCallback(S32, LLScrollbar*); + + void show_hide_scrollbar (const LLRect& child_rect); + void showScrollbar (const LLRect& child_rect); + void hideScrollbar (const LLRect& child_rect); + + void updateLayout ( const LLRect& child_rect ); + void ctrlSetLeftTopAndSize (LLView* panel, S32 left, S32 top, S32 width, S32 height); + + void drawChild(const LLRect& root_rect,LLView* child); + + LLView* findContainerView (); + + void selectOnFocusReceived(); + void deselectOnFocusLost(); + +private: + + class LLAccordionCtrlTabHeader; + LLAccordionCtrlTabHeader* mHeader; //Header + + bool mDisplayChildren; //Expanded/collapsed + bool mCollapsible; + bool mHeaderVisible; + + bool mCanOpenClose; + bool mFitPanel; + + S32 mPaddingLeft; + S32 mPaddingRight; + S32 mPaddingTop; + S32 mPaddingBottom; + + bool mStoredOpenCloseState; + bool mWasStateStored; + bool mSkipChangesOnNotifyParent; + + bool mSelectionEnabled; + + LLScrollbar* mScrollbar; + LLView* mContainerPanel; + + LLUIColor mDropdownBGColor; +}; + +#endif diff --git a/indra/llui/llbadge.cpp b/indra/llui/llbadge.cpp index 1fa24afe8c..3397c97ee1 100644 --- a/indra/llui/llbadge.cpp +++ b/indra/llui/llbadge.cpp @@ -1,389 +1,389 @@ -/** - * @file llbadge.cpp - * @brief Implementation for badges - * - * $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$ - */ - -#define LLBADGE_CPP -#include "llbadge.h" - -#include "llscrollcontainer.h" -#include "lluictrlfactory.h" - - -static LLDefaultChildRegistry::Register r("badge"); - -static const S32 BADGE_OFFSET_NOT_SPECIFIED = 0x7FFFFFFF; - -// Compiler optimization, generate extern template -template class LLBadge* LLView::getChild(const std::string& name, bool recurse) const; - - -LLBadge::Params::Params() - : image("image") - , border_image("border_image") - , border_color("border_color") - , image_color("image_color") - , label("label") - , label_color("label_color") - , label_offset_horiz("label_offset_horiz") - , label_offset_vert("label_offset_vert") - , location("location", LLRelPos::TOP_LEFT) - , location_offset_hcenter("location_offset_hcenter") - , location_offset_vcenter("location_offset_vcenter") - , location_percent_hcenter("location_percent_hcenter") - , location_percent_vcenter("location_percent_vcenter") - , padding_horiz("padding_horiz") - , padding_vert("padding_vert") -{} - -bool LLBadge::Params::equals(const Params& a) const -{ - bool comp = true; - - // skip owner in comparison on purpose - - comp &= (border_image() == a.border_image()); - comp &= (border_color() == a.border_color()); - comp &= (image() == a.image()); - comp &= (image_color() == a.image_color()); - comp &= (label() == a.label()); - comp &= (label_color() == a.label_color()); - comp &= (label_offset_horiz() == a.label_offset_horiz()); - comp &= (label_offset_vert() == a.label_offset_vert()); - comp &= (location() == a.location()); - comp &= (location_offset_hcenter() == a.location_offset_hcenter()); - comp &= (location_offset_vcenter() == a.location_offset_vcenter()); - comp &= (location_percent_hcenter() == a.location_percent_hcenter()); - comp &= (location_percent_vcenter() == a.location_percent_vcenter()); - comp &= (padding_horiz() == a.padding_horiz()); - comp &= (padding_vert() == a.padding_vert()); - - return comp; -} - -LLBadge::LLBadge(const LLBadge::Params& p) - : LLUICtrl(p) - , mOwner(p.owner) - , mBorderImage(p.border_image) - , mBorderColor(p.border_color) - , mGLFont(p.font) - , mImage(p.image) - , mImageColor(p.image_color) - , mLabel(p.label) - , mLabelColor(p.label_color) - , mLabelOffsetHoriz(p.label_offset_horiz) - , mLabelOffsetVert(p.label_offset_vert) - , mLocation(p.location) - , mLocationOffsetHCenter(BADGE_OFFSET_NOT_SPECIFIED) - , mLocationOffsetVCenter(BADGE_OFFSET_NOT_SPECIFIED) - , mLocationPercentHCenter(0.5f) - , mLocationPercentVCenter(0.5f) - , mPaddingHoriz(p.padding_horiz) - , mPaddingVert(p.padding_vert) - , mParentScroller(NULL) - , mDrawAtParentTop(false) -{ - if (mImage.isNull()) - { - LL_WARNS() << "Badge: " << getName() << " with no image!" << LL_ENDL; - } - - if (p.location_offset_hcenter.isProvided()) - { - mLocationOffsetHCenter = p.location_offset_hcenter(); - } - - if (p.location_offset_vcenter.isProvided()) - { - mLocationOffsetVCenter = p.location_offset_vcenter(); - } - - // - // The following logic is to set the mLocationPercentHCenter and mLocationPercentVCenter - // based on the Location enum and our horizontal and vertical location percentages. The - // draw code then uses this on the owner rectangle to compute the screen location for - // the badge. - // - - if (!LLRelPos::IsCenter(mLocation)) - { - F32 h_center = p.location_percent_hcenter * 0.01f; - F32 v_center = p.location_percent_vcenter * 0.01f; - - if (LLRelPos::IsRight(mLocation)) - { - mLocationPercentHCenter = 0.5f * (1.0f + h_center); - } - else if (LLRelPos::IsLeft(mLocation)) - { - mLocationPercentHCenter = 0.5f * (1.0f - h_center); - } - - if (LLRelPos::IsTop(mLocation)) - { - mLocationPercentVCenter = 0.5f * (1.0f + v_center); - } - else if (LLRelPos::IsBottom(mLocation)) - { - mLocationPercentVCenter = 0.5f * (1.0f - v_center); - } - } -} - -LLBadge::~LLBadge() -{ -} - -bool LLBadge::addToView(LLView * view) -{ - bool child_added = view->addChild(this); - - if (child_added) - { - setShape(view->getLocalRect()); - - // Find a parent scroll container, if there is one in case we need it for positioning - - LLView * parent = mOwner.get(); - - while ((parent != NULL) && ((mParentScroller = dynamic_cast(parent)) == NULL)) - { - parent = parent->getParent(); - } - } - - return child_added; -} - -void LLBadge::setLabel(const LLStringExplicit& label) -{ - mLabel = label; -} - -// -// This is a fallback function to render a rectangle for badges without a valid image -// -void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, const LLColor4U &color) -{ - gGL.pushUIMatrix(); - gGL.loadUIIdentity(); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.color4ubv(color.mV); - gGL.texCoord2i(0, 0); - - F32 x = LLFontGL::sCurOrigin.mX + centerX - width * 0.5f; - F32 y = LLFontGL::sCurOrigin.mY + centerY - height * 0.5f; - - LLRectf screen_rect(ll_round(x), - ll_round(y), - ll_round(x) + width, - ll_round(y) + height); - - LLVector3 vertices[4]; - vertices[0] = LLVector3(screen_rect.mRight, screen_rect.mTop, 1.0f); - vertices[1] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 1.0f); - vertices[2] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 1.0f); - vertices[3] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 1.0f); - - gGL.begin(LLRender::QUADS); - { - gGL.vertexBatchPreTransformed(vertices, 4); - } - gGL.end(); - - gGL.popUIMatrix(); -} - - -// virtual -void LLBadge::draw() -{ - if (!mLabel.empty()) - { - LLView* owner_view = mOwner.get(); - - if (owner_view && owner_view->isInVisibleChain()) - { - // - // Calculate badge size based on label text - // - - LLWString badge_label_wstring = mLabel; - - S32 badge_label_begin_offset = 0; - S32 badge_char_length = S32_MAX; - S32 badge_pixel_length = S32_MAX; - F32 *right_position_out = NULL; - bool do_not_use_ellipses = false; - - F32 badge_width = (2.0f * mPaddingHoriz) + - mGLFont->getWidthF32(badge_label_wstring.c_str(), badge_label_begin_offset, badge_char_length); - - F32 badge_height = (2.0f * mPaddingVert) + mGLFont->getLineHeight(); - - // - // Calculate badge position based on owner - // - - LLRect owner_rect; - owner_view->localRectToOtherView(owner_view->getLocalRect(), & owner_rect, this); - - S32 location_offset_horiz = mLocationOffsetHCenter; - S32 location_offset_vert = mLocationOffsetVCenter; - - // If we're in a scroll container, do some math to keep us in the same place on screen if applicable - if (mParentScroller != NULL) - { - LLRect visibleRect = mParentScroller->getVisibleContentRect(); - - if (mLocationOffsetHCenter != BADGE_OFFSET_NOT_SPECIFIED) - { - if (LLRelPos::IsRight(mLocation)) - { - location_offset_horiz += visibleRect.mRight; - } - else if (LLRelPos::IsLeft(mLocation)) - { - location_offset_horiz += visibleRect.mLeft; - } - else // center - { - location_offset_horiz += (visibleRect.mLeft + visibleRect.mRight) / 2; - } - } - - if (mLocationOffsetVCenter != BADGE_OFFSET_NOT_SPECIFIED) - { - if (LLRelPos::IsTop(mLocation)) - { - location_offset_vert += visibleRect.mTop; - } - else if (LLRelPos::IsBottom(mLocation)) - { - location_offset_vert += visibleRect.mBottom; - } - else // center - { - location_offset_vert += (visibleRect.mBottom + visibleRect.mTop) / 2; - } - } - } - - F32 badge_center_x; - F32 badge_center_y; - - // Compute x position - if (mLocationOffsetHCenter == BADGE_OFFSET_NOT_SPECIFIED) - { - badge_center_x = owner_rect.mLeft + owner_rect.getWidth() * mLocationPercentHCenter; - } - else - { - badge_center_x = location_offset_horiz; - } - - // Compute y position - if (mLocationOffsetVCenter == BADGE_OFFSET_NOT_SPECIFIED) - { - if(mDrawAtParentTop) - { - badge_center_y = owner_rect.mTop - badge_height * 0.5f - 1; - } - else - { - badge_center_y = owner_rect.mBottom + owner_rect.getHeight() * mLocationPercentVCenter; - } - } - else - { - badge_center_y = location_offset_vert; - } - - // - // Draw button image, if available. - // Otherwise draw basic rectangular button. - // - - F32 alpha = getDrawContext().mAlpha; - - if (!mImage.isNull()) - { - F32 badge_x = badge_center_x - badge_width * 0.5f; - F32 badge_y = badge_center_y - badge_height * 0.5f; - - mImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mImageColor % alpha); - - if (!mBorderImage.isNull()) - { - mBorderImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mBorderColor % alpha); - } - } - else - { - LL_DEBUGS() << "No image for badge " << getName() << " on owner " << owner_view->getName() << LL_ENDL; - - renderBadgeBackground(badge_center_x, badge_center_y, - badge_width, badge_height, - mImageColor % alpha); - } - - // - // Draw the label - // - - mGLFont->render(badge_label_wstring, - badge_label_begin_offset, - badge_center_x + mLabelOffsetHoriz, - badge_center_y + mLabelOffsetVert, - mLabelColor % alpha, - LLFontGL::HCENTER, LLFontGL::VCENTER, // centered around the position - LLFontGL::NORMAL, // normal text (not bold, italics, etc.) - LLFontGL::DROP_SHADOW_SOFT, - badge_char_length, badge_pixel_length, - right_position_out, do_not_use_ellipses); - } - } -} - - -namespace LLInitParam -{ - void TypeValues::declareValues() - { - declare("bottom", LLRelPos::BOTTOM); - declare("bottom_left", LLRelPos::BOTTOM_LEFT); - declare("bottom_right", LLRelPos::BOTTOM_RIGHT); - declare("center", LLRelPos::CENTER); - declare("left", LLRelPos::LEFT); - declare("right", LLRelPos::RIGHT); - declare("top", LLRelPos::TOP); - declare("top_left", LLRelPos::TOP_LEFT); - declare("top_right", LLRelPos::TOP_RIGHT); - } -} - - -// eof +/** + * @file llbadge.cpp + * @brief Implementation for badges + * + * $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$ + */ + +#define LLBADGE_CPP +#include "llbadge.h" + +#include "llscrollcontainer.h" +#include "lluictrlfactory.h" + + +static LLDefaultChildRegistry::Register r("badge"); + +static const S32 BADGE_OFFSET_NOT_SPECIFIED = 0x7FFFFFFF; + +// Compiler optimization, generate extern template +template class LLBadge* LLView::getChild(const std::string& name, bool recurse) const; + + +LLBadge::Params::Params() + : image("image") + , border_image("border_image") + , border_color("border_color") + , image_color("image_color") + , label("label") + , label_color("label_color") + , label_offset_horiz("label_offset_horiz") + , label_offset_vert("label_offset_vert") + , location("location", LLRelPos::TOP_LEFT) + , location_offset_hcenter("location_offset_hcenter") + , location_offset_vcenter("location_offset_vcenter") + , location_percent_hcenter("location_percent_hcenter") + , location_percent_vcenter("location_percent_vcenter") + , padding_horiz("padding_horiz") + , padding_vert("padding_vert") +{} + +bool LLBadge::Params::equals(const Params& a) const +{ + bool comp = true; + + // skip owner in comparison on purpose + + comp &= (border_image() == a.border_image()); + comp &= (border_color() == a.border_color()); + comp &= (image() == a.image()); + comp &= (image_color() == a.image_color()); + comp &= (label() == a.label()); + comp &= (label_color() == a.label_color()); + comp &= (label_offset_horiz() == a.label_offset_horiz()); + comp &= (label_offset_vert() == a.label_offset_vert()); + comp &= (location() == a.location()); + comp &= (location_offset_hcenter() == a.location_offset_hcenter()); + comp &= (location_offset_vcenter() == a.location_offset_vcenter()); + comp &= (location_percent_hcenter() == a.location_percent_hcenter()); + comp &= (location_percent_vcenter() == a.location_percent_vcenter()); + comp &= (padding_horiz() == a.padding_horiz()); + comp &= (padding_vert() == a.padding_vert()); + + return comp; +} + +LLBadge::LLBadge(const LLBadge::Params& p) + : LLUICtrl(p) + , mOwner(p.owner) + , mBorderImage(p.border_image) + , mBorderColor(p.border_color) + , mGLFont(p.font) + , mImage(p.image) + , mImageColor(p.image_color) + , mLabel(p.label) + , mLabelColor(p.label_color) + , mLabelOffsetHoriz(p.label_offset_horiz) + , mLabelOffsetVert(p.label_offset_vert) + , mLocation(p.location) + , mLocationOffsetHCenter(BADGE_OFFSET_NOT_SPECIFIED) + , mLocationOffsetVCenter(BADGE_OFFSET_NOT_SPECIFIED) + , mLocationPercentHCenter(0.5f) + , mLocationPercentVCenter(0.5f) + , mPaddingHoriz(p.padding_horiz) + , mPaddingVert(p.padding_vert) + , mParentScroller(NULL) + , mDrawAtParentTop(false) +{ + if (mImage.isNull()) + { + LL_WARNS() << "Badge: " << getName() << " with no image!" << LL_ENDL; + } + + if (p.location_offset_hcenter.isProvided()) + { + mLocationOffsetHCenter = p.location_offset_hcenter(); + } + + if (p.location_offset_vcenter.isProvided()) + { + mLocationOffsetVCenter = p.location_offset_vcenter(); + } + + // + // The following logic is to set the mLocationPercentHCenter and mLocationPercentVCenter + // based on the Location enum and our horizontal and vertical location percentages. The + // draw code then uses this on the owner rectangle to compute the screen location for + // the badge. + // + + if (!LLRelPos::IsCenter(mLocation)) + { + F32 h_center = p.location_percent_hcenter * 0.01f; + F32 v_center = p.location_percent_vcenter * 0.01f; + + if (LLRelPos::IsRight(mLocation)) + { + mLocationPercentHCenter = 0.5f * (1.0f + h_center); + } + else if (LLRelPos::IsLeft(mLocation)) + { + mLocationPercentHCenter = 0.5f * (1.0f - h_center); + } + + if (LLRelPos::IsTop(mLocation)) + { + mLocationPercentVCenter = 0.5f * (1.0f + v_center); + } + else if (LLRelPos::IsBottom(mLocation)) + { + mLocationPercentVCenter = 0.5f * (1.0f - v_center); + } + } +} + +LLBadge::~LLBadge() +{ +} + +bool LLBadge::addToView(LLView * view) +{ + bool child_added = view->addChild(this); + + if (child_added) + { + setShape(view->getLocalRect()); + + // Find a parent scroll container, if there is one in case we need it for positioning + + LLView * parent = mOwner.get(); + + while ((parent != NULL) && ((mParentScroller = dynamic_cast(parent)) == NULL)) + { + parent = parent->getParent(); + } + } + + return child_added; +} + +void LLBadge::setLabel(const LLStringExplicit& label) +{ + mLabel = label; +} + +// +// This is a fallback function to render a rectangle for badges without a valid image +// +void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, const LLColor4U &color) +{ + gGL.pushUIMatrix(); + gGL.loadUIIdentity(); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + gGL.color4ubv(color.mV); + gGL.texCoord2i(0, 0); + + F32 x = LLFontGL::sCurOrigin.mX + centerX - width * 0.5f; + F32 y = LLFontGL::sCurOrigin.mY + centerY - height * 0.5f; + + LLRectf screen_rect(ll_round(x), + ll_round(y), + ll_round(x) + width, + ll_round(y) + height); + + LLVector3 vertices[4]; + vertices[0] = LLVector3(screen_rect.mRight, screen_rect.mTop, 1.0f); + vertices[1] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 1.0f); + vertices[2] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 1.0f); + vertices[3] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 1.0f); + + gGL.begin(LLRender::QUADS); + { + gGL.vertexBatchPreTransformed(vertices, 4); + } + gGL.end(); + + gGL.popUIMatrix(); +} + + +// virtual +void LLBadge::draw() +{ + if (!mLabel.empty()) + { + LLView* owner_view = mOwner.get(); + + if (owner_view && owner_view->isInVisibleChain()) + { + // + // Calculate badge size based on label text + // + + LLWString badge_label_wstring = mLabel; + + S32 badge_label_begin_offset = 0; + S32 badge_char_length = S32_MAX; + S32 badge_pixel_length = S32_MAX; + F32 *right_position_out = NULL; + bool do_not_use_ellipses = false; + + F32 badge_width = (2.0f * mPaddingHoriz) + + mGLFont->getWidthF32(badge_label_wstring.c_str(), badge_label_begin_offset, badge_char_length); + + F32 badge_height = (2.0f * mPaddingVert) + mGLFont->getLineHeight(); + + // + // Calculate badge position based on owner + // + + LLRect owner_rect; + owner_view->localRectToOtherView(owner_view->getLocalRect(), & owner_rect, this); + + S32 location_offset_horiz = mLocationOffsetHCenter; + S32 location_offset_vert = mLocationOffsetVCenter; + + // If we're in a scroll container, do some math to keep us in the same place on screen if applicable + if (mParentScroller != NULL) + { + LLRect visibleRect = mParentScroller->getVisibleContentRect(); + + if (mLocationOffsetHCenter != BADGE_OFFSET_NOT_SPECIFIED) + { + if (LLRelPos::IsRight(mLocation)) + { + location_offset_horiz += visibleRect.mRight; + } + else if (LLRelPos::IsLeft(mLocation)) + { + location_offset_horiz += visibleRect.mLeft; + } + else // center + { + location_offset_horiz += (visibleRect.mLeft + visibleRect.mRight) / 2; + } + } + + if (mLocationOffsetVCenter != BADGE_OFFSET_NOT_SPECIFIED) + { + if (LLRelPos::IsTop(mLocation)) + { + location_offset_vert += visibleRect.mTop; + } + else if (LLRelPos::IsBottom(mLocation)) + { + location_offset_vert += visibleRect.mBottom; + } + else // center + { + location_offset_vert += (visibleRect.mBottom + visibleRect.mTop) / 2; + } + } + } + + F32 badge_center_x; + F32 badge_center_y; + + // Compute x position + if (mLocationOffsetHCenter == BADGE_OFFSET_NOT_SPECIFIED) + { + badge_center_x = owner_rect.mLeft + owner_rect.getWidth() * mLocationPercentHCenter; + } + else + { + badge_center_x = location_offset_horiz; + } + + // Compute y position + if (mLocationOffsetVCenter == BADGE_OFFSET_NOT_SPECIFIED) + { + if(mDrawAtParentTop) + { + badge_center_y = owner_rect.mTop - badge_height * 0.5f - 1; + } + else + { + badge_center_y = owner_rect.mBottom + owner_rect.getHeight() * mLocationPercentVCenter; + } + } + else + { + badge_center_y = location_offset_vert; + } + + // + // Draw button image, if available. + // Otherwise draw basic rectangular button. + // + + F32 alpha = getDrawContext().mAlpha; + + if (!mImage.isNull()) + { + F32 badge_x = badge_center_x - badge_width * 0.5f; + F32 badge_y = badge_center_y - badge_height * 0.5f; + + mImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mImageColor % alpha); + + if (!mBorderImage.isNull()) + { + mBorderImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mBorderColor % alpha); + } + } + else + { + LL_DEBUGS() << "No image for badge " << getName() << " on owner " << owner_view->getName() << LL_ENDL; + + renderBadgeBackground(badge_center_x, badge_center_y, + badge_width, badge_height, + mImageColor % alpha); + } + + // + // Draw the label + // + + mGLFont->render(badge_label_wstring, + badge_label_begin_offset, + badge_center_x + mLabelOffsetHoriz, + badge_center_y + mLabelOffsetVert, + mLabelColor % alpha, + LLFontGL::HCENTER, LLFontGL::VCENTER, // centered around the position + LLFontGL::NORMAL, // normal text (not bold, italics, etc.) + LLFontGL::DROP_SHADOW_SOFT, + badge_char_length, badge_pixel_length, + right_position_out, do_not_use_ellipses); + } + } +} + + +namespace LLInitParam +{ + void TypeValues::declareValues() + { + declare("bottom", LLRelPos::BOTTOM); + declare("bottom_left", LLRelPos::BOTTOM_LEFT); + declare("bottom_right", LLRelPos::BOTTOM_RIGHT); + declare("center", LLRelPos::CENTER); + declare("left", LLRelPos::LEFT); + declare("right", LLRelPos::RIGHT); + declare("top", LLRelPos::TOP); + declare("top_left", LLRelPos::TOP_LEFT); + declare("top_right", LLRelPos::TOP_RIGHT); + } +} + + +// eof diff --git a/indra/llui/llbadge.h b/indra/llui/llbadge.h index d4b146d57b..a6d584c6d7 100644 --- a/indra/llui/llbadge.h +++ b/indra/llui/llbadge.h @@ -1,177 +1,177 @@ -/** - * @file llbadge.h - * @brief Header for badges - * - * $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$ - */ - -#ifndef LL_LLBADGE_H -#define LL_LLBADGE_H - -#include - -#include "lluicolor.h" -#include "lluictrl.h" -#include "llstring.h" -#include "lluiimage.h" -#include "llview.h" - -// -// Declarations -// - -class LLFontGL; -class LLScrollContainer; -class LLUICtrlFactory; - -// -// Relative Position Alignment -// - -namespace LLRelPos -{ - enum Location - { - CENTER = 0, - - LEFT = (1 << 0), - RIGHT = (1 << 1), - - TOP = (1 << 2), - BOTTOM = (1 << 3), - - BOTTOM_LEFT = (BOTTOM | LEFT), - BOTTOM_RIGHT = (BOTTOM | RIGHT), - - TOP_LEFT = (TOP | LEFT), - TOP_RIGHT = (TOP | RIGHT), - }; - - inline bool IsBottom(Location relPos) { return (relPos & BOTTOM) == BOTTOM; } - inline bool IsCenter(Location relPos) { return (relPos == CENTER); } - inline bool IsLeft(Location relPos) { return (relPos & LEFT) == LEFT; } - inline bool IsRight(Location relPos) { return (relPos & RIGHT) == RIGHT; } - inline bool IsTop(Location relPos) { return (relPos & TOP) == TOP; } -} - -// NOTE: This needs to occur before Optional declaration for proper compilation. -namespace LLInitParam -{ - template<> - struct TypeValues : public TypeValuesHelper - { - static void declareValues(); - }; -} - -// -// Classes -// - -class LLBadge -: public LLUICtrl -{ -public: - struct Params - : public LLInitParam::Block - { - Optional< LLHandle > owner; // Mandatory in code but not in xml - - Optional< LLUIImage* > border_image; - Optional< LLUIColor > border_color; - - Optional< LLUIImage* > image; - Optional< LLUIColor > image_color; - - Optional< std::string > label; - Optional< LLUIColor > label_color; - - Optional< S32 > label_offset_horiz; - Optional< S32 > label_offset_vert; - - Optional< LLRelPos::Location > location; - Optional< S32 > location_offset_hcenter; - Optional< S32 > location_offset_vcenter; - Optional< U32 > location_percent_hcenter; - Optional< U32 > location_percent_vcenter; - - Optional< F32 > padding_horiz; - Optional< F32 > padding_vert; - - Params(); - - bool equals(const Params&) const; - }; - -protected: - friend class LLUICtrlFactory; - LLBadge(const Params& p); - -public: - - ~LLBadge(); - - bool addToView(LLView * view); - - virtual void draw(); - - const std::string getLabel() const { return wstring_to_utf8str(mLabel); } - void setLabel( const LLStringExplicit& label); - - void setDrawAtParentTop(bool draw_at_top) { mDrawAtParentTop = draw_at_top;} - -private: - LLPointer< LLUIImage > mBorderImage; - LLUIColor mBorderColor; - - const LLFontGL* mGLFont; - - LLPointer< LLUIImage > mImage; - LLUIColor mImageColor; - - LLUIString mLabel; - LLUIColor mLabelColor; - - S32 mLabelOffsetHoriz; - S32 mLabelOffsetVert; - - LLRelPos::Location mLocation; - S32 mLocationOffsetHCenter; - S32 mLocationOffsetVCenter; - F32 mLocationPercentHCenter; - F32 mLocationPercentVCenter; - - LLHandle< LLView > mOwner; - - F32 mPaddingHoriz; - F32 mPaddingVert; - - LLScrollContainer* mParentScroller; - bool mDrawAtParentTop; -}; - -// Build time optimization, generate once in .cpp file -#ifndef LLBADGE_CPP -extern template class LLBadge* LLView::getChild(const std::string& name, bool recurse) const; -#endif - -#endif // LL_LLBADGE_H +/** + * @file llbadge.h + * @brief Header for badges + * + * $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$ + */ + +#ifndef LL_LLBADGE_H +#define LL_LLBADGE_H + +#include + +#include "lluicolor.h" +#include "lluictrl.h" +#include "llstring.h" +#include "lluiimage.h" +#include "llview.h" + +// +// Declarations +// + +class LLFontGL; +class LLScrollContainer; +class LLUICtrlFactory; + +// +// Relative Position Alignment +// + +namespace LLRelPos +{ + enum Location + { + CENTER = 0, + + LEFT = (1 << 0), + RIGHT = (1 << 1), + + TOP = (1 << 2), + BOTTOM = (1 << 3), + + BOTTOM_LEFT = (BOTTOM | LEFT), + BOTTOM_RIGHT = (BOTTOM | RIGHT), + + TOP_LEFT = (TOP | LEFT), + TOP_RIGHT = (TOP | RIGHT), + }; + + inline bool IsBottom(Location relPos) { return (relPos & BOTTOM) == BOTTOM; } + inline bool IsCenter(Location relPos) { return (relPos == CENTER); } + inline bool IsLeft(Location relPos) { return (relPos & LEFT) == LEFT; } + inline bool IsRight(Location relPos) { return (relPos & RIGHT) == RIGHT; } + inline bool IsTop(Location relPos) { return (relPos & TOP) == TOP; } +} + +// NOTE: This needs to occur before Optional declaration for proper compilation. +namespace LLInitParam +{ + template<> + struct TypeValues : public TypeValuesHelper + { + static void declareValues(); + }; +} + +// +// Classes +// + +class LLBadge +: public LLUICtrl +{ +public: + struct Params + : public LLInitParam::Block + { + Optional< LLHandle > owner; // Mandatory in code but not in xml + + Optional< LLUIImage* > border_image; + Optional< LLUIColor > border_color; + + Optional< LLUIImage* > image; + Optional< LLUIColor > image_color; + + Optional< std::string > label; + Optional< LLUIColor > label_color; + + Optional< S32 > label_offset_horiz; + Optional< S32 > label_offset_vert; + + Optional< LLRelPos::Location > location; + Optional< S32 > location_offset_hcenter; + Optional< S32 > location_offset_vcenter; + Optional< U32 > location_percent_hcenter; + Optional< U32 > location_percent_vcenter; + + Optional< F32 > padding_horiz; + Optional< F32 > padding_vert; + + Params(); + + bool equals(const Params&) const; + }; + +protected: + friend class LLUICtrlFactory; + LLBadge(const Params& p); + +public: + + ~LLBadge(); + + bool addToView(LLView * view); + + virtual void draw(); + + const std::string getLabel() const { return wstring_to_utf8str(mLabel); } + void setLabel( const LLStringExplicit& label); + + void setDrawAtParentTop(bool draw_at_top) { mDrawAtParentTop = draw_at_top;} + +private: + LLPointer< LLUIImage > mBorderImage; + LLUIColor mBorderColor; + + const LLFontGL* mGLFont; + + LLPointer< LLUIImage > mImage; + LLUIColor mImageColor; + + LLUIString mLabel; + LLUIColor mLabelColor; + + S32 mLabelOffsetHoriz; + S32 mLabelOffsetVert; + + LLRelPos::Location mLocation; + S32 mLocationOffsetHCenter; + S32 mLocationOffsetVCenter; + F32 mLocationPercentHCenter; + F32 mLocationPercentVCenter; + + LLHandle< LLView > mOwner; + + F32 mPaddingHoriz; + F32 mPaddingVert; + + LLScrollContainer* mParentScroller; + bool mDrawAtParentTop; +}; + +// Build time optimization, generate once in .cpp file +#ifndef LLBADGE_CPP +extern template class LLBadge* LLView::getChild(const std::string& name, bool recurse) const; +#endif + +#endif // LL_LLBADGE_H diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index b343f5b4b4..e6c045250e 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -1,1321 +1,1321 @@ - -/** - * @file llbutton.cpp - * @brief LLButton base class - * - * $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 "linden_common.h" - -#define LLBUTTON_CPP -#include "llbutton.h" - -// Linden library includes -#include "v4color.h" -#include "llstring.h" - -// Project includes -#include "llkeyboard.h" -#include "llui.h" -#include "lluiconstants.h" -#include "llresmgr.h" -#include "llcriticaldamp.h" -#include "llfloater.h" -#include "llfloaterreg.h" -#include "llfocusmgr.h" -#include "llwindow.h" -#include "llnotificationsutil.h" -#include "llrender.h" -#include "lluictrlfactory.h" -#include "lluiusage.h" -#include "llhelp.h" -#include "lldockablefloater.h" -#include "llviewereventrecorder.h" - -static LLDefaultChildRegistry::Register r("button"); - -// Compiler optimization, generate extern template -template class LLButton* LLView::getChild( - const std::string& name, bool recurse) const; - -// globals -S32 LLBUTTON_H_PAD = 4; -S32 BTN_HEIGHT_SMALL= 23; -S32 BTN_HEIGHT = 23; -S32 BTN_DROP_SHADOW = 2; - -LLButton::Params::Params() -: label_selected("label_selected"), // requires is_toggle true - label_shadow("label_shadow", true), - auto_resize("auto_resize", false), - use_ellipses("use_ellipses", false), - use_font_color("use_font_color", true), - image_unselected("image_unselected"), - image_selected("image_selected"), - image_hover_selected("image_hover_selected"), - image_hover_unselected("image_hover_unselected"), - image_disabled_selected("image_disabled_selected"), - image_disabled("image_disabled"), - image_pressed("image_pressed"), - image_pressed_selected("image_pressed_selected"), - image_overlay("image_overlay"), - image_overlay_alignment("image_overlay_alignment", std::string("center")), - image_top_pad("image_top_pad"), - image_bottom_pad("image_bottom_pad"), - imgoverlay_label_space("imgoverlay_label_space", 1), - label_color("label_color"), - label_color_selected("label_color_selected"), // requires is_toggle true - label_color_disabled("label_color_disabled"), - label_color_disabled_selected("label_color_disabled_selected"), - image_color("image_color"), - image_color_disabled("image_color_disabled"), - image_overlay_color("image_overlay_color", LLColor4::white % 0.75f), - image_overlay_disabled_color("image_overlay_disabled_color", LLColor4::white % 0.3f), - image_overlay_selected_color("image_overlay_selected_color", LLColor4::white), - flash_color("flash_color"), - pad_right("pad_right", LLBUTTON_H_PAD), - pad_left("pad_left", LLBUTTON_H_PAD), - pad_bottom("pad_bottom"), - click_callback("click_callback"), - mouse_down_callback("mouse_down_callback"), - mouse_up_callback("mouse_up_callback"), - mouse_held_callback("mouse_held_callback"), - is_toggle("is_toggle", false), - scale_image("scale_image", true), - hover_glow_amount("hover_glow_amount"), - commit_on_return("commit_on_return", true), - commit_on_capture_lost("commit_on_capture_lost", false), - display_pressed_state("display_pressed_state", true), - use_draw_context_alpha("use_draw_context_alpha", true), - badge("badge"), - handle_right_mouse("handle_right_mouse"), - held_down_delay("held_down_delay"), - button_flash_enable("button_flash_enable", false), - button_flash_count("button_flash_count"), - button_flash_rate("button_flash_rate") -{ - addSynonym(is_toggle, "toggle"); - changeDefault(initial_value, LLSD(false)); -} - - -LLButton::LLButton(const LLButton::Params& p) -: LLUICtrl(p), - LLBadgeOwner(getHandle()), - mMouseDownFrame(0), - mMouseHeldDownCount(0), - mBorderEnabled( false ), - mFlashing( false ), - mCurGlowStrength(0.f), - mNeedsHighlight(false), - mUnselectedLabel(p.label()), - mSelectedLabel(p.label_selected()), - mGLFont(p.font), - mHeldDownDelay(p.held_down_delay.seconds), // seconds until held-down callback is called - mHeldDownFrameDelay(p.held_down_delay.frames), - mImageUnselected(p.image_unselected), - mImageSelected(p.image_selected), - mImageDisabled(p.image_disabled), - mImageDisabledSelected(p.image_disabled_selected), - mImageFlash(p.image_flash), - mImagePressed(p.image_pressed), - mImagePressedSelected(p.image_pressed_selected), - mImageHoverSelected(p.image_hover_selected), - mImageHoverUnselected(p.image_hover_unselected), - mUnselectedLabelColor(p.label_color()), - mSelectedLabelColor(p.label_color_selected()), - mDisabledLabelColor(p.label_color_disabled()), - mDisabledSelectedLabelColor(p.label_color_disabled_selected()), - mImageColor(p.image_color()), - mFlashBgColor(p.flash_color()), - mDisabledImageColor(p.image_color_disabled()), - mImageOverlay(p.image_overlay()), - mImageOverlayColor(p.image_overlay_color()), - mImageOverlayDisabledColor(p.image_overlay_disabled_color()), - mImageOverlaySelectedColor(p.image_overlay_selected_color()), - mImageOverlayAlignment(LLFontGL::hAlignFromName(p.image_overlay_alignment)), - mImageOverlayTopPad(p.image_top_pad), - mImageOverlayBottomPad(p.image_bottom_pad), - mImgOverlayLabelSpace(p.imgoverlay_label_space), - mIsToggle(p.is_toggle), - mScaleImage(p.scale_image), - mDropShadowedText(p.label_shadow), - mAutoResize(p.auto_resize), - mUseEllipses( p.use_ellipses ), - mUseFontColor( p.use_font_color), - mHAlign(p.font_halign), - mLeftHPad(p.pad_left), - mRightHPad(p.pad_right), - mBottomVPad(p.pad_bottom), - mHoverGlowStrength(p.hover_glow_amount), - mCommitOnReturn(p.commit_on_return), - mCommitOnCaptureLost(p.commit_on_capture_lost), - mFadeWhenDisabled(false), - mForcePressedState(false), - mDisplayPressedState(p.display_pressed_state), - mLastDrawCharsCount(0), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mHeldDownSignal(NULL), - mUseDrawContextAlpha(p.use_draw_context_alpha), - mHandleRightMouse(p.handle_right_mouse), - mFlashingTimer(NULL) -{ - if (p.button_flash_enable) - { - // If optional parameter "p.button_flash_count" is not provided, LLFlashTimer will be - // used instead it a "default" value from gSavedSettings.getS32("FlashCount")). - // Likewise, missing "p.button_flash_rate" is replaced by gSavedSettings.getF32("FlashPeriod"). - // Note: flashing should be allowed in settings.xml (boolean key "EnableButtonFlashing"). - S32 flash_count = p.button_flash_count.isProvided()? p.button_flash_count : 0; - F32 flash_rate = p.button_flash_rate.isProvided()? p.button_flash_rate : 0.0; - mFlashingTimer = new LLFlashTimer ((LLFlashTimer::callback_t)NULL, flash_count, flash_rate); - } - else - { - mButtonFlashCount = p.button_flash_count; - mButtonFlashRate = p.button_flash_rate; - } - - static LLUICachedControl llbutton_orig_h_pad ("UIButtonOrigHPad", 0); - static Params default_params(LLUICtrlFactory::getDefaultParams()); - - if (!p.label_selected.isProvided()) - { - mSelectedLabel = mUnselectedLabel; - } - - // Hack to make sure there is space for at least one character - if (getRect().mRight >= 0 && getRect().getWidth() > 0 && - getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) - { - // Use old defaults - mLeftHPad = llbutton_orig_h_pad; - mRightHPad = llbutton_orig_h_pad; - } - - mMouseDownTimer.stop(); - - // if custom unselected button image provided... - if (p.image_unselected != default_params.image_unselected) - { - //...fade it out for disabled image by default... - if (p.image_disabled() == default_params.image_disabled() ) - { - mImageDisabled = p.image_unselected; - mFadeWhenDisabled = true; - } - - if (p.image_pressed_selected == default_params.image_pressed_selected) - { - mImagePressedSelected = mImageUnselected; - } - } - - // if custom selected button image provided... - if (p.image_selected != default_params.image_selected) - { - //...fade it out for disabled image by default... - if (p.image_disabled_selected() == default_params.image_disabled_selected()) - { - mImageDisabledSelected = p.image_selected; - mFadeWhenDisabled = true; - } - - if (p.image_pressed == default_params.image_pressed) - { - mImagePressed = mImageSelected; - } - } - - if (!p.image_pressed.isProvided()) - { - mImagePressed = mImageSelected; - } - - if (!p.image_pressed_selected.isProvided()) - { - mImagePressedSelected = mImageUnselected; - } - - if (mImageUnselected.isNull()) - { - LL_WARNS() << "Button: " << getName() << " with no image!" << LL_ENDL; - } - - if (p.click_callback.isProvided()) - { - setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback - } - if (p.mouse_down_callback.isProvided()) - { - setMouseDownCallback(initCommitCallback(p.mouse_down_callback)); - } - if (p.mouse_up_callback.isProvided()) - { - setMouseUpCallback(initCommitCallback(p.mouse_up_callback)); - } - if (p.mouse_held_callback.isProvided()) - { - setHeldDownCallback(initCommitCallback(p.mouse_held_callback)); - } - - if (p.badge.isProvided()) - { - LLBadgeOwner::initBadgeParams(p.badge()); - } -} - -LLButton::~LLButton() -{ - delete mMouseDownSignal; - delete mMouseUpSignal; - delete mHeldDownSignal; - - if (mFlashingTimer) - { - mFlashingTimer->unset(); - } -} - -// HACK: Committing a button is the same as instantly clicking it. -// virtual -void LLButton::onCommit() -{ - // WARNING: Sometimes clicking a button destroys the floater or - // panel containing it. Therefore we need to call LLUICtrl::onCommit() - // LAST, otherwise this becomes deleted memory. - - if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD()); - - if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); - - if (getSoundFlags() & MOUSE_DOWN) - { - make_ui_sound("UISndClick"); - } - - if (getSoundFlags() & MOUSE_UP) - { - make_ui_sound("UISndClickRelease"); - } - - if (mIsToggle) - { - toggleState(); - } - - // do this last, as it can result in destroying this button - 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::postBuild() -{ - autoResize(); - - addBadgeToParentHolder(); - - return LLUICtrl::postBuild(); -} - -bool LLButton::handleUnicodeCharHere(llwchar uni_char) -{ - bool handled = false; - if(' ' == uni_char - && !gKeyboard->getKeyRepeated(' ')) - { - if (mIsToggle) - { - toggleState(); - } - - LLUICtrl::onCommit(); - - handled = true; - } - return handled; -} - -bool LLButton::handleKeyHere(KEY key, MASK mask ) -{ - bool handled = false; - if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) - { - if (mIsToggle) - { - toggleState(); - } - - handled = true; - - LLUICtrl::onCommit(); - } - return handled; -} - - -bool LLButton::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (!childrenHandleMouseDown(x, y, mask)) - { - // Route future Mouse messages here preemptively. (Release on mouse up.) - gFocusMgr.setMouseCapture( this ); - - if (hasTabStop() && !getIsChrome()) - { - setFocus(true); - } - - if (!mFunctionName.empty()) - { - LL_DEBUGS("UIUsage") << "calling mouse down function " << mFunctionName << LL_ENDL; - LLUIUsage::instance().logCommand(mFunctionName); - LLUIUsage::instance().logControl(getPathname()); - } - - /* - * 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); - - LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); - - if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD()); - - mMouseDownTimer.start(); - mMouseDownFrame = (S32) LLFrameTimer::getFrameCount(); - mMouseHeldDownCount = 0; - - - if (getSoundFlags() & MOUSE_DOWN) - { - make_ui_sound("UISndClick"); - } - } - return true; -} - - -bool LLButton::handleMouseUp(S32 x, S32 y, MASK mask) -{ - // We only handle the click if the click both started and ended within us - if( hasMouseCapture() ) - { - // reset timers before focus change, to not cause - // additional commits if mCommitOnCaptureLost. - resetMouseDownTimer(); - - // Always release the mouse - gFocusMgr.setMouseCapture( NULL ); - - /* - * 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); - LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); - - // Regardless of where mouseup occurs, handle callback - if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); - - // 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 - if (pointInView(x, y)) - { - if (getSoundFlags() & MOUSE_UP) - { - make_ui_sound("UISndClickRelease"); - } - - if (mIsToggle) - { - toggleState(); - } - - 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::onMouseLeave(S32 x, S32 y, MASK mask) -{ - LLUICtrl::onMouseLeave(x, y, mask); - - mNeedsHighlight = false; -} - -void LLButton::setHighlight(bool b) -{ - mNeedsHighlight = b; -} - -bool LLButton::handleHover(S32 x, S32 y, MASK mask) -{ - if (isInEnabledChain() - && (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)) - mNeedsHighlight = true; - - if (!childrenHandleHover(x, y, mask)) - { - if (mMouseDownTimer.getStarted()) - { - 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); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL; - } - 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 = ll_round((F32)overlay_width * scale_factor); - overlay_height = ll_round((F32)overlay_height * scale_factor); -} - - -// virtual -void LLButton::draw() -{ - static LLCachedControl sEnableButtonFlashing(*LLUI::getInstance()->mSettingGroups["config"], "EnableButtonFlashing", true); - F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); - - bool pressed_by_keyboard = false; - if (hasFocus()) - { - pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); - } - - bool mouse_pressed_and_over = false; - if (hasMouseCapture()) - { - S32 local_mouse_x ; - S32 local_mouse_y; - LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); - mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y); - } - - bool enabled = isInEnabledChain(); - - bool pressed = pressed_by_keyboard - || mouse_pressed_and_over - || mForcePressedState; - bool selected = getToggleState(); - - bool use_glow_effect = false; - LLColor4 highlighting_color = LLColor4::white; - LLColor4 glow_color = LLColor4::white; - LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; - LLUIImage* imagep = NULL; - LLUIImage* image_glow = NULL; - - // Cancel sticking of color, if the button is pressed, - // or when a flashing of the previously selected button is ended - if (mFlashingTimer - && ((selected && !mFlashingTimer->isFlashingInProgress() && !mForceFlashing) || pressed)) - { - mFlashing = false; - } - - bool flash = mFlashing && sEnableButtonFlashing; - - if (pressed && mDisplayPressedState) - { - imagep = selected ? mImagePressedSelected : mImagePressed; - } - else if ( mNeedsHighlight ) - { - if (selected) - { - if (mImageHoverSelected) - { - imagep = mImageHoverSelected; - } - else - { - imagep = mImageSelected; - use_glow_effect = true; - } - } - else - { - if (mImageHoverUnselected) - { - imagep = mImageHoverUnselected; - } - else - { - imagep = mImageUnselected; - use_glow_effect = true; - } - } - } - else - { - imagep = selected ? mImageSelected : mImageUnselected; - } - - // Override if more data is available - // HACK: Use gray checked state to mean either: - // enabled and tentative - // or - // disabled but checked - if (!mImageDisabledSelected.isNull() - && - ( (enabled && getTentative()) - || (!enabled && selected ) ) ) - { - imagep = mImageDisabledSelected; - } - else if (!mImageDisabled.isNull() - && !enabled - && !selected) - { - imagep = mImageDisabled; - } - - image_glow = imagep; - - if (mFlashing) - { - if (flash && mImageFlash) - { - // if button should flash and we have icon for flashing, use it as image for button - image_glow = mImageFlash; - } - - // provide fade-in and fade-out via flash_color - if (mFlashingTimer) - { - LLColor4 flash_color = mFlashBgColor.get(); - use_glow_effect = true; - glow_type = LLRender::BT_ALPHA; // blend the glow - - if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress()) - { - glow_color = flash_color; - } - else if (mNeedsHighlight) - { - glow_color = highlighting_color; - } - else - { - // will fade from highlight color - glow_color = flash_color; - } - } - } - - if (mNeedsHighlight && !imagep) - { - use_glow_effect = true; - } - - // Figure out appropriate color for the text - LLColor4 label_color; - - // label changes when button state changes, not when pressed - if ( enabled ) - { - if ( getToggleState() ) - { - label_color = mSelectedLabelColor.get(); - } - else - { - label_color = mUnselectedLabelColor.get(); - } - } - else - { - if ( getToggleState() ) - { - label_color = mDisabledSelectedLabelColor.get(); - } - else - { - label_color = mDisabledLabelColor.get(); - } - } - - // Highlight if needed - if( ll::ui::SearchableControl::getHighlighted() ) - label_color = ll::ui::SearchableControl::getHighlightColor(); - - // Unselected label assignments - LLWString label = getCurrentLabel(); - - // overlay with keyboard focus border - if (hasFocus()) - { - F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); - drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, ll_round(lerp(1.f, 3.f, lerp_amt))); - } - - if (use_glow_effect) - { - mCurGlowStrength = lerp(mCurGlowStrength, - mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.f : 0.f) : mHoverGlowStrength, - LLSmoothInterpolation::getInterpolant(0.05f)); - } - else - { - mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f)); - } - - // Draw button image, if available. - // Otherwise draw basic rectangular button. - if (imagep != NULL) - { - // apply automatic 50% alpha fade to disabled image - LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get(); - if ( mScaleImage) - { - imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha ); - if (mCurGlowStrength > 0.01f) - { - gGL.setSceneBlendType(glow_type); - image_glow->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha)); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - } - else - { - S32 y = getLocalRect().getHeight() - imagep->getHeight(); - imagep->draw(0, y, (enabled ? mImageColor.get() : disabled_color) % alpha); - if (mCurGlowStrength > 0.01f) - { - gGL.setSceneBlendType(glow_type); - image_glow->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha)); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - } - } - else - { - // no image - LL_DEBUGS() << "No image for button " << getName() << LL_ENDL; - // draw it in pink so we can find it - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, false); - } - - // let overlay image and text play well together - S32 text_left = mLeftHPad; - S32 text_right = getRect().getWidth() - mRightHPad; - S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad; - - // draw overlay image - if (mImageOverlay.notNull()) - { - // get max width and height (discard level 0) - S32 overlay_width; - S32 overlay_height; - - getOverlayImageSize(overlay_width, overlay_height); - - S32 center_x = getLocalRect().getCenterX(); - S32 center_y = getLocalRect().getCenterY(); - - //FUGLY HACK FOR "DEPRESSED" BUTTONS - if (pressed && mDisplayPressedState) - { - center_y--; - center_x++; - } - - center_y += (mImageOverlayBottomPad - mImageOverlayTopPad); - // fade out overlay images on disabled buttons - LLColor4 overlay_color = mImageOverlayColor.get(); - if (!enabled) - { - 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 + mImgOverlayLabelSpace; - text_width -= overlay_width + mImgOverlayLabelSpace; - mImageOverlay->draw( - mLeftHPad, - center_y - (overlay_height / 2), - overlay_width, - overlay_height, - overlay_color); - break; - case LLFontGL::HCENTER: - mImageOverlay->draw( - center_x - (overlay_width / 2), - center_y - (overlay_height / 2), - overlay_width, - overlay_height, - overlay_color); - break; - case LLFontGL::RIGHT: - text_right -= overlay_width + mImgOverlayLabelSpace; - text_width -= overlay_width + mImgOverlayLabelSpace; - mImageOverlay->draw( - getRect().getWidth() - mRightHPad - overlay_width, - center_y - (overlay_height / 2), - overlay_width, - overlay_height, - overlay_color); - break; - default: - // draw nothing - break; - } - } - - // Draw label - if( !label.empty() ) - { - LLWStringUtil::trim(label); - - S32 x; - switch( mHAlign ) - { - case LLFontGL::RIGHT: - x = text_right; - break; - case LLFontGL::HCENTER: - x = text_left + (text_width / 2); - break; - case LLFontGL::LEFT: - default: - x = text_left; - break; - } - - if (pressed && mDisplayPressedState) - { - x++; - } - - // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as - // max_chars. - // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value. - // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode. - // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars. - mLastDrawCharsCount = mGLFont->render(label, 0, - (F32)x, - (F32)(getRect().getHeight() / 2 + mBottomVPad), - label_color % alpha, - mHAlign, LLFontGL::VCENTER, - LLFontGL::NORMAL, - mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW, - S32_MAX, text_width, - NULL, mUseEllipses, mUseFontColor); - } - - LLUICtrl::draw(); -} - -void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size) -{ - if (imagep == NULL) return; - if (mScaleImage) - { - imagep->drawBorder(getLocalRect(), color, size); - } - else - { - S32 y = getLocalRect().getHeight() - imagep->getHeight(); - imagep->drawBorder(0, y, color, size); - } -} - -bool LLButton::getToggleState() const -{ - return getValue().asBoolean(); -} - -void LLButton::setToggleState(bool b) -{ - if( b != getToggleState() ) - { - setControlValue(b); // will fire LLControlVariable callbacks (if any) - setValue(b); // may or may not be redundant - setFlashing(false); // stop flash state whenever the selected/unselected state if reset - // Unselected label assignments - autoResize(); - } -} - -void LLButton::setFlashing(bool b, bool force_flashing/* = false */) -{ - mForceFlashing = force_flashing; - if (mFlashingTimer) - { - mFlashing = b; - (b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing()); - } - else if (b != mFlashing) - { - mFlashing = b; - mFrameTimer.reset(); - } -} - -bool LLButton::toggleState() -{ - bool flipped = ! getToggleState(); - setToggleState(flipped); - - return flipped; -} - -void LLButton::setLabel( const std::string& label ) -{ - mUnselectedLabel = mSelectedLabel = label; -} - -void LLButton::setLabel( const LLUIString& label ) -{ - mUnselectedLabel = mSelectedLabel = label; -} - -void LLButton::setLabel( const LLStringExplicit& label ) -{ - setLabelUnselected(label); - setLabelSelected(label); -} - -//virtual -bool LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - mUnselectedLabel.setArg(key, text); - mSelectedLabel.setArg(key, text); - return true; -} - -void LLButton::setLabelUnselected( const LLStringExplicit& label ) -{ - mUnselectedLabel = label; -} - -void LLButton::setLabelSelected( const LLStringExplicit& label ) -{ - mSelectedLabel = label; -} - -bool LLButton::labelIsTruncated() const -{ - return getCurrentLabel().getString().size() > mLastDrawCharsCount; -} - -const LLUIString& LLButton::getCurrentLabel() const -{ - return getToggleState() ? mSelectedLabel : mUnselectedLabel; -} - -void LLButton::setImageUnselected(LLPointer image) -{ - mImageUnselected = image; - if (mImageUnselected.isNull()) - { - LL_WARNS() << "Setting default button image for: " << getName() << " to NULL" << LL_ENDL; - } -} - -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 = ll_round((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(LLUI::getUIImage(image_name)); - setImageSelected(LLUI::getUIImage(selected_name)); -} - -void LLButton::setImageSelected(LLPointer image) -{ - mImageSelected = image; -} - -void LLButton::setImageColor(const LLColor4& c) -{ - mImageColor = c; -} - -void LLButton::setColor(const LLColor4& color) -{ - setImageColor(color); -} - -void LLButton::setImageDisabled(LLPointer image) -{ - mImageDisabled = image; - mDisabledImageColor = mImageColor; - mFadeWhenDisabled = true; -} - -void LLButton::setImageDisabledSelected(LLPointer image) -{ - mImageDisabledSelected = image; - mDisabledImageColor = mImageColor; - mFadeWhenDisabled = true; -} - -void LLButton::setImagePressed(LLPointer image) -{ - mImagePressed = image; -} - -void LLButton::setImageHoverSelected(LLPointer image) -{ - mImageHoverSelected = image; -} - -void LLButton::setImageHoverUnselected(LLPointer image) -{ - mImageHoverUnselected = image; -} - -void LLButton::setImageFlash(LLPointer image) -{ - mImageFlash = image; -} - -void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color) -{ - if (image_name.empty()) - { - mImageOverlay = NULL; - } - else - { - mImageOverlay = LLUI::getUIImage(image_name); - mImageOverlayAlignment = alignment; - mImageOverlayColor = color; - } -} - -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() -{ - if (mCommitOnCaptureLost - && mMouseDownTimer.getStarted()) - { - if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); - - if (mIsToggle) - { - toggleState(); - } - - LLUICtrl::onCommit(); - } - resetMouseDownTimer(); -} - -//------------------------------------------------------------------------- -// Utilities -//------------------------------------------------------------------------- -S32 round_up(S32 grid, S32 value) -{ - S32 mod = value % grid; - - if (mod > 0) - { - // not even multiple - return value + (grid - mod); - } - else - { - return value; - } -} - -void LLButton::addImageAttributeToXML(LLXMLNodePtr node, - const std::string& image_name, - const LLUUID& image_id, - const std::string& xml_tag_name) const -{ - if( !image_name.empty() ) - { - 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); - } -} - - -// static -void LLButton::toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname) -{ - bool floater_vis = LLFloaterReg::toggleInstance(sdname.asString()); - LLButton* button = dynamic_cast(ctrl); - if (button) - button->setToggleState(floater_vis); -} - -// static -// Gets called once -void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname) -{ - LLButton* button = dynamic_cast(ctrl); - if (!button) - return; - // Get the visibility control name for the floater - std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString()); - // Set the button control value (toggle state) to the floater visibility control (Sets the value as well) - button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name)); - // Set the clicked callback to toggle the floater - button->setClickedCallback(boost::bind(&LLFloaterReg::toggleInstance, sdname, LLSD())); -} - -// static -void LLButton::setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname) -{ - LLButton* button = dynamic_cast(ctrl); - if (!button) - return; - // Get the visibility control name for the floater - std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString()); - // Set the button control value (toggle state) to the floater visibility control (Sets the value as well) - button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name)); - // Set the clicked callback to toggle the floater - button->setClickedCallback(boost::bind(&LLDockableFloater::toggleInstance, sdname)); -} - -// static -void LLButton::showHelp(LLUICtrl* ctrl, const LLSD& sdname) -{ - // search back through the button's parents for a panel - // with a help_topic string defined - std::string help_topic; - if (LLUI::getInstance()->mHelpImpl && - ctrl->findHelpTopic(help_topic)) - { - LLUI::getInstance()->mHelpImpl->showTopic(help_topic); - return; // success - } - - // display an error if we can't find a help_topic string. - // fix this by adding a help_topic attribute to the xui file - LLNotificationsUtil::add("UnableToFindHelpTopic"); -} - -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); -} + +/** + * @file llbutton.cpp + * @brief LLButton base class + * + * $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 "linden_common.h" + +#define LLBUTTON_CPP +#include "llbutton.h" + +// Linden library includes +#include "v4color.h" +#include "llstring.h" + +// Project includes +#include "llkeyboard.h" +#include "llui.h" +#include "lluiconstants.h" +#include "llresmgr.h" +#include "llcriticaldamp.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "llfocusmgr.h" +#include "llwindow.h" +#include "llnotificationsutil.h" +#include "llrender.h" +#include "lluictrlfactory.h" +#include "lluiusage.h" +#include "llhelp.h" +#include "lldockablefloater.h" +#include "llviewereventrecorder.h" + +static LLDefaultChildRegistry::Register r("button"); + +// Compiler optimization, generate extern template +template class LLButton* LLView::getChild( + const std::string& name, bool recurse) const; + +// globals +S32 LLBUTTON_H_PAD = 4; +S32 BTN_HEIGHT_SMALL= 23; +S32 BTN_HEIGHT = 23; +S32 BTN_DROP_SHADOW = 2; + +LLButton::Params::Params() +: label_selected("label_selected"), // requires is_toggle true + label_shadow("label_shadow", true), + auto_resize("auto_resize", false), + use_ellipses("use_ellipses", false), + use_font_color("use_font_color", true), + image_unselected("image_unselected"), + image_selected("image_selected"), + image_hover_selected("image_hover_selected"), + image_hover_unselected("image_hover_unselected"), + image_disabled_selected("image_disabled_selected"), + image_disabled("image_disabled"), + image_pressed("image_pressed"), + image_pressed_selected("image_pressed_selected"), + image_overlay("image_overlay"), + image_overlay_alignment("image_overlay_alignment", std::string("center")), + image_top_pad("image_top_pad"), + image_bottom_pad("image_bottom_pad"), + imgoverlay_label_space("imgoverlay_label_space", 1), + label_color("label_color"), + label_color_selected("label_color_selected"), // requires is_toggle true + label_color_disabled("label_color_disabled"), + label_color_disabled_selected("label_color_disabled_selected"), + image_color("image_color"), + image_color_disabled("image_color_disabled"), + image_overlay_color("image_overlay_color", LLColor4::white % 0.75f), + image_overlay_disabled_color("image_overlay_disabled_color", LLColor4::white % 0.3f), + image_overlay_selected_color("image_overlay_selected_color", LLColor4::white), + flash_color("flash_color"), + pad_right("pad_right", LLBUTTON_H_PAD), + pad_left("pad_left", LLBUTTON_H_PAD), + pad_bottom("pad_bottom"), + click_callback("click_callback"), + mouse_down_callback("mouse_down_callback"), + mouse_up_callback("mouse_up_callback"), + mouse_held_callback("mouse_held_callback"), + is_toggle("is_toggle", false), + scale_image("scale_image", true), + hover_glow_amount("hover_glow_amount"), + commit_on_return("commit_on_return", true), + commit_on_capture_lost("commit_on_capture_lost", false), + display_pressed_state("display_pressed_state", true), + use_draw_context_alpha("use_draw_context_alpha", true), + badge("badge"), + handle_right_mouse("handle_right_mouse"), + held_down_delay("held_down_delay"), + button_flash_enable("button_flash_enable", false), + button_flash_count("button_flash_count"), + button_flash_rate("button_flash_rate") +{ + addSynonym(is_toggle, "toggle"); + changeDefault(initial_value, LLSD(false)); +} + + +LLButton::LLButton(const LLButton::Params& p) +: LLUICtrl(p), + LLBadgeOwner(getHandle()), + mMouseDownFrame(0), + mMouseHeldDownCount(0), + mBorderEnabled( false ), + mFlashing( false ), + mCurGlowStrength(0.f), + mNeedsHighlight(false), + mUnselectedLabel(p.label()), + mSelectedLabel(p.label_selected()), + mGLFont(p.font), + mHeldDownDelay(p.held_down_delay.seconds), // seconds until held-down callback is called + mHeldDownFrameDelay(p.held_down_delay.frames), + mImageUnselected(p.image_unselected), + mImageSelected(p.image_selected), + mImageDisabled(p.image_disabled), + mImageDisabledSelected(p.image_disabled_selected), + mImageFlash(p.image_flash), + mImagePressed(p.image_pressed), + mImagePressedSelected(p.image_pressed_selected), + mImageHoverSelected(p.image_hover_selected), + mImageHoverUnselected(p.image_hover_unselected), + mUnselectedLabelColor(p.label_color()), + mSelectedLabelColor(p.label_color_selected()), + mDisabledLabelColor(p.label_color_disabled()), + mDisabledSelectedLabelColor(p.label_color_disabled_selected()), + mImageColor(p.image_color()), + mFlashBgColor(p.flash_color()), + mDisabledImageColor(p.image_color_disabled()), + mImageOverlay(p.image_overlay()), + mImageOverlayColor(p.image_overlay_color()), + mImageOverlayDisabledColor(p.image_overlay_disabled_color()), + mImageOverlaySelectedColor(p.image_overlay_selected_color()), + mImageOverlayAlignment(LLFontGL::hAlignFromName(p.image_overlay_alignment)), + mImageOverlayTopPad(p.image_top_pad), + mImageOverlayBottomPad(p.image_bottom_pad), + mImgOverlayLabelSpace(p.imgoverlay_label_space), + mIsToggle(p.is_toggle), + mScaleImage(p.scale_image), + mDropShadowedText(p.label_shadow), + mAutoResize(p.auto_resize), + mUseEllipses( p.use_ellipses ), + mUseFontColor( p.use_font_color), + mHAlign(p.font_halign), + mLeftHPad(p.pad_left), + mRightHPad(p.pad_right), + mBottomVPad(p.pad_bottom), + mHoverGlowStrength(p.hover_glow_amount), + mCommitOnReturn(p.commit_on_return), + mCommitOnCaptureLost(p.commit_on_capture_lost), + mFadeWhenDisabled(false), + mForcePressedState(false), + mDisplayPressedState(p.display_pressed_state), + mLastDrawCharsCount(0), + mMouseDownSignal(NULL), + mMouseUpSignal(NULL), + mHeldDownSignal(NULL), + mUseDrawContextAlpha(p.use_draw_context_alpha), + mHandleRightMouse(p.handle_right_mouse), + mFlashingTimer(NULL) +{ + if (p.button_flash_enable) + { + // If optional parameter "p.button_flash_count" is not provided, LLFlashTimer will be + // used instead it a "default" value from gSavedSettings.getS32("FlashCount")). + // Likewise, missing "p.button_flash_rate" is replaced by gSavedSettings.getF32("FlashPeriod"). + // Note: flashing should be allowed in settings.xml (boolean key "EnableButtonFlashing"). + S32 flash_count = p.button_flash_count.isProvided()? p.button_flash_count : 0; + F32 flash_rate = p.button_flash_rate.isProvided()? p.button_flash_rate : 0.0; + mFlashingTimer = new LLFlashTimer ((LLFlashTimer::callback_t)NULL, flash_count, flash_rate); + } + else + { + mButtonFlashCount = p.button_flash_count; + mButtonFlashRate = p.button_flash_rate; + } + + static LLUICachedControl llbutton_orig_h_pad ("UIButtonOrigHPad", 0); + static Params default_params(LLUICtrlFactory::getDefaultParams()); + + if (!p.label_selected.isProvided()) + { + mSelectedLabel = mUnselectedLabel; + } + + // Hack to make sure there is space for at least one character + if (getRect().mRight >= 0 && getRect().getWidth() > 0 && + getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) + { + // Use old defaults + mLeftHPad = llbutton_orig_h_pad; + mRightHPad = llbutton_orig_h_pad; + } + + mMouseDownTimer.stop(); + + // if custom unselected button image provided... + if (p.image_unselected != default_params.image_unselected) + { + //...fade it out for disabled image by default... + if (p.image_disabled() == default_params.image_disabled() ) + { + mImageDisabled = p.image_unselected; + mFadeWhenDisabled = true; + } + + if (p.image_pressed_selected == default_params.image_pressed_selected) + { + mImagePressedSelected = mImageUnselected; + } + } + + // if custom selected button image provided... + if (p.image_selected != default_params.image_selected) + { + //...fade it out for disabled image by default... + if (p.image_disabled_selected() == default_params.image_disabled_selected()) + { + mImageDisabledSelected = p.image_selected; + mFadeWhenDisabled = true; + } + + if (p.image_pressed == default_params.image_pressed) + { + mImagePressed = mImageSelected; + } + } + + if (!p.image_pressed.isProvided()) + { + mImagePressed = mImageSelected; + } + + if (!p.image_pressed_selected.isProvided()) + { + mImagePressedSelected = mImageUnselected; + } + + if (mImageUnselected.isNull()) + { + LL_WARNS() << "Button: " << getName() << " with no image!" << LL_ENDL; + } + + if (p.click_callback.isProvided()) + { + setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback + } + if (p.mouse_down_callback.isProvided()) + { + setMouseDownCallback(initCommitCallback(p.mouse_down_callback)); + } + if (p.mouse_up_callback.isProvided()) + { + setMouseUpCallback(initCommitCallback(p.mouse_up_callback)); + } + if (p.mouse_held_callback.isProvided()) + { + setHeldDownCallback(initCommitCallback(p.mouse_held_callback)); + } + + if (p.badge.isProvided()) + { + LLBadgeOwner::initBadgeParams(p.badge()); + } +} + +LLButton::~LLButton() +{ + delete mMouseDownSignal; + delete mMouseUpSignal; + delete mHeldDownSignal; + + if (mFlashingTimer) + { + mFlashingTimer->unset(); + } +} + +// HACK: Committing a button is the same as instantly clicking it. +// virtual +void LLButton::onCommit() +{ + // WARNING: Sometimes clicking a button destroys the floater or + // panel containing it. Therefore we need to call LLUICtrl::onCommit() + // LAST, otherwise this becomes deleted memory. + + if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD()); + + if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); + + if (getSoundFlags() & MOUSE_DOWN) + { + make_ui_sound("UISndClick"); + } + + if (getSoundFlags() & MOUSE_UP) + { + make_ui_sound("UISndClickRelease"); + } + + if (mIsToggle) + { + toggleState(); + } + + // do this last, as it can result in destroying this button + 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::postBuild() +{ + autoResize(); + + addBadgeToParentHolder(); + + return LLUICtrl::postBuild(); +} + +bool LLButton::handleUnicodeCharHere(llwchar uni_char) +{ + bool handled = false; + if(' ' == uni_char + && !gKeyboard->getKeyRepeated(' ')) + { + if (mIsToggle) + { + toggleState(); + } + + LLUICtrl::onCommit(); + + handled = true; + } + return handled; +} + +bool LLButton::handleKeyHere(KEY key, MASK mask ) +{ + bool handled = false; + if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) + { + if (mIsToggle) + { + toggleState(); + } + + handled = true; + + LLUICtrl::onCommit(); + } + return handled; +} + + +bool LLButton::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (!childrenHandleMouseDown(x, y, mask)) + { + // Route future Mouse messages here preemptively. (Release on mouse up.) + gFocusMgr.setMouseCapture( this ); + + if (hasTabStop() && !getIsChrome()) + { + setFocus(true); + } + + if (!mFunctionName.empty()) + { + LL_DEBUGS("UIUsage") << "calling mouse down function " << mFunctionName << LL_ENDL; + LLUIUsage::instance().logCommand(mFunctionName); + LLUIUsage::instance().logControl(getPathname()); + } + + /* + * 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); + + LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); + + if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD()); + + mMouseDownTimer.start(); + mMouseDownFrame = (S32) LLFrameTimer::getFrameCount(); + mMouseHeldDownCount = 0; + + + if (getSoundFlags() & MOUSE_DOWN) + { + make_ui_sound("UISndClick"); + } + } + return true; +} + + +bool LLButton::handleMouseUp(S32 x, S32 y, MASK mask) +{ + // We only handle the click if the click both started and ended within us + if( hasMouseCapture() ) + { + // reset timers before focus change, to not cause + // additional commits if mCommitOnCaptureLost. + resetMouseDownTimer(); + + // Always release the mouse + gFocusMgr.setMouseCapture( NULL ); + + /* + * 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); + LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); + + // Regardless of where mouseup occurs, handle callback + if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); + + // 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 + if (pointInView(x, y)) + { + if (getSoundFlags() & MOUSE_UP) + { + make_ui_sound("UISndClickRelease"); + } + + if (mIsToggle) + { + toggleState(); + } + + 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::onMouseLeave(S32 x, S32 y, MASK mask) +{ + LLUICtrl::onMouseLeave(x, y, mask); + + mNeedsHighlight = false; +} + +void LLButton::setHighlight(bool b) +{ + mNeedsHighlight = b; +} + +bool LLButton::handleHover(S32 x, S32 y, MASK mask) +{ + if (isInEnabledChain() + && (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)) + mNeedsHighlight = true; + + if (!childrenHandleHover(x, y, mask)) + { + if (mMouseDownTimer.getStarted()) + { + 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); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL; + } + 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 = ll_round((F32)overlay_width * scale_factor); + overlay_height = ll_round((F32)overlay_height * scale_factor); +} + + +// virtual +void LLButton::draw() +{ + static LLCachedControl sEnableButtonFlashing(*LLUI::getInstance()->mSettingGroups["config"], "EnableButtonFlashing", true); + F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); + + bool pressed_by_keyboard = false; + if (hasFocus()) + { + pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); + } + + bool mouse_pressed_and_over = false; + if (hasMouseCapture()) + { + S32 local_mouse_x ; + S32 local_mouse_y; + LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); + mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y); + } + + bool enabled = isInEnabledChain(); + + bool pressed = pressed_by_keyboard + || mouse_pressed_and_over + || mForcePressedState; + bool selected = getToggleState(); + + bool use_glow_effect = false; + LLColor4 highlighting_color = LLColor4::white; + LLColor4 glow_color = LLColor4::white; + LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; + LLUIImage* imagep = NULL; + LLUIImage* image_glow = NULL; + + // Cancel sticking of color, if the button is pressed, + // or when a flashing of the previously selected button is ended + if (mFlashingTimer + && ((selected && !mFlashingTimer->isFlashingInProgress() && !mForceFlashing) || pressed)) + { + mFlashing = false; + } + + bool flash = mFlashing && sEnableButtonFlashing; + + if (pressed && mDisplayPressedState) + { + imagep = selected ? mImagePressedSelected : mImagePressed; + } + else if ( mNeedsHighlight ) + { + if (selected) + { + if (mImageHoverSelected) + { + imagep = mImageHoverSelected; + } + else + { + imagep = mImageSelected; + use_glow_effect = true; + } + } + else + { + if (mImageHoverUnselected) + { + imagep = mImageHoverUnselected; + } + else + { + imagep = mImageUnselected; + use_glow_effect = true; + } + } + } + else + { + imagep = selected ? mImageSelected : mImageUnselected; + } + + // Override if more data is available + // HACK: Use gray checked state to mean either: + // enabled and tentative + // or + // disabled but checked + if (!mImageDisabledSelected.isNull() + && + ( (enabled && getTentative()) + || (!enabled && selected ) ) ) + { + imagep = mImageDisabledSelected; + } + else if (!mImageDisabled.isNull() + && !enabled + && !selected) + { + imagep = mImageDisabled; + } + + image_glow = imagep; + + if (mFlashing) + { + if (flash && mImageFlash) + { + // if button should flash and we have icon for flashing, use it as image for button + image_glow = mImageFlash; + } + + // provide fade-in and fade-out via flash_color + if (mFlashingTimer) + { + LLColor4 flash_color = mFlashBgColor.get(); + use_glow_effect = true; + glow_type = LLRender::BT_ALPHA; // blend the glow + + if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress()) + { + glow_color = flash_color; + } + else if (mNeedsHighlight) + { + glow_color = highlighting_color; + } + else + { + // will fade from highlight color + glow_color = flash_color; + } + } + } + + if (mNeedsHighlight && !imagep) + { + use_glow_effect = true; + } + + // Figure out appropriate color for the text + LLColor4 label_color; + + // label changes when button state changes, not when pressed + if ( enabled ) + { + if ( getToggleState() ) + { + label_color = mSelectedLabelColor.get(); + } + else + { + label_color = mUnselectedLabelColor.get(); + } + } + else + { + if ( getToggleState() ) + { + label_color = mDisabledSelectedLabelColor.get(); + } + else + { + label_color = mDisabledLabelColor.get(); + } + } + + // Highlight if needed + if( ll::ui::SearchableControl::getHighlighted() ) + label_color = ll::ui::SearchableControl::getHighlightColor(); + + // Unselected label assignments + LLWString label = getCurrentLabel(); + + // overlay with keyboard focus border + if (hasFocus()) + { + F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); + drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, ll_round(lerp(1.f, 3.f, lerp_amt))); + } + + if (use_glow_effect) + { + mCurGlowStrength = lerp(mCurGlowStrength, + mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.f : 0.f) : mHoverGlowStrength, + LLSmoothInterpolation::getInterpolant(0.05f)); + } + else + { + mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f)); + } + + // Draw button image, if available. + // Otherwise draw basic rectangular button. + if (imagep != NULL) + { + // apply automatic 50% alpha fade to disabled image + LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get(); + if ( mScaleImage) + { + imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha ); + if (mCurGlowStrength > 0.01f) + { + gGL.setSceneBlendType(glow_type); + image_glow->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha)); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + } + else + { + S32 y = getLocalRect().getHeight() - imagep->getHeight(); + imagep->draw(0, y, (enabled ? mImageColor.get() : disabled_color) % alpha); + if (mCurGlowStrength > 0.01f) + { + gGL.setSceneBlendType(glow_type); + image_glow->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha)); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + } + } + else + { + // no image + LL_DEBUGS() << "No image for button " << getName() << LL_ENDL; + // draw it in pink so we can find it + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, false); + } + + // let overlay image and text play well together + S32 text_left = mLeftHPad; + S32 text_right = getRect().getWidth() - mRightHPad; + S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad; + + // draw overlay image + if (mImageOverlay.notNull()) + { + // get max width and height (discard level 0) + S32 overlay_width; + S32 overlay_height; + + getOverlayImageSize(overlay_width, overlay_height); + + S32 center_x = getLocalRect().getCenterX(); + S32 center_y = getLocalRect().getCenterY(); + + //FUGLY HACK FOR "DEPRESSED" BUTTONS + if (pressed && mDisplayPressedState) + { + center_y--; + center_x++; + } + + center_y += (mImageOverlayBottomPad - mImageOverlayTopPad); + // fade out overlay images on disabled buttons + LLColor4 overlay_color = mImageOverlayColor.get(); + if (!enabled) + { + 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 + mImgOverlayLabelSpace; + text_width -= overlay_width + mImgOverlayLabelSpace; + mImageOverlay->draw( + mLeftHPad, + center_y - (overlay_height / 2), + overlay_width, + overlay_height, + overlay_color); + break; + case LLFontGL::HCENTER: + mImageOverlay->draw( + center_x - (overlay_width / 2), + center_y - (overlay_height / 2), + overlay_width, + overlay_height, + overlay_color); + break; + case LLFontGL::RIGHT: + text_right -= overlay_width + mImgOverlayLabelSpace; + text_width -= overlay_width + mImgOverlayLabelSpace; + mImageOverlay->draw( + getRect().getWidth() - mRightHPad - overlay_width, + center_y - (overlay_height / 2), + overlay_width, + overlay_height, + overlay_color); + break; + default: + // draw nothing + break; + } + } + + // Draw label + if( !label.empty() ) + { + LLWStringUtil::trim(label); + + S32 x; + switch( mHAlign ) + { + case LLFontGL::RIGHT: + x = text_right; + break; + case LLFontGL::HCENTER: + x = text_left + (text_width / 2); + break; + case LLFontGL::LEFT: + default: + x = text_left; + break; + } + + if (pressed && mDisplayPressedState) + { + x++; + } + + // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as + // max_chars. + // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value. + // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode. + // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars. + mLastDrawCharsCount = mGLFont->render(label, 0, + (F32)x, + (F32)(getRect().getHeight() / 2 + mBottomVPad), + label_color % alpha, + mHAlign, LLFontGL::VCENTER, + LLFontGL::NORMAL, + mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW, + S32_MAX, text_width, + NULL, mUseEllipses, mUseFontColor); + } + + LLUICtrl::draw(); +} + +void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size) +{ + if (imagep == NULL) return; + if (mScaleImage) + { + imagep->drawBorder(getLocalRect(), color, size); + } + else + { + S32 y = getLocalRect().getHeight() - imagep->getHeight(); + imagep->drawBorder(0, y, color, size); + } +} + +bool LLButton::getToggleState() const +{ + return getValue().asBoolean(); +} + +void LLButton::setToggleState(bool b) +{ + if( b != getToggleState() ) + { + setControlValue(b); // will fire LLControlVariable callbacks (if any) + setValue(b); // may or may not be redundant + setFlashing(false); // stop flash state whenever the selected/unselected state if reset + // Unselected label assignments + autoResize(); + } +} + +void LLButton::setFlashing(bool b, bool force_flashing/* = false */) +{ + mForceFlashing = force_flashing; + if (mFlashingTimer) + { + mFlashing = b; + (b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing()); + } + else if (b != mFlashing) + { + mFlashing = b; + mFrameTimer.reset(); + } +} + +bool LLButton::toggleState() +{ + bool flipped = ! getToggleState(); + setToggleState(flipped); + + return flipped; +} + +void LLButton::setLabel( const std::string& label ) +{ + mUnselectedLabel = mSelectedLabel = label; +} + +void LLButton::setLabel( const LLUIString& label ) +{ + mUnselectedLabel = mSelectedLabel = label; +} + +void LLButton::setLabel( const LLStringExplicit& label ) +{ + setLabelUnselected(label); + setLabelSelected(label); +} + +//virtual +bool LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + mUnselectedLabel.setArg(key, text); + mSelectedLabel.setArg(key, text); + return true; +} + +void LLButton::setLabelUnselected( const LLStringExplicit& label ) +{ + mUnselectedLabel = label; +} + +void LLButton::setLabelSelected( const LLStringExplicit& label ) +{ + mSelectedLabel = label; +} + +bool LLButton::labelIsTruncated() const +{ + return getCurrentLabel().getString().size() > mLastDrawCharsCount; +} + +const LLUIString& LLButton::getCurrentLabel() const +{ + return getToggleState() ? mSelectedLabel : mUnselectedLabel; +} + +void LLButton::setImageUnselected(LLPointer image) +{ + mImageUnselected = image; + if (mImageUnselected.isNull()) + { + LL_WARNS() << "Setting default button image for: " << getName() << " to NULL" << LL_ENDL; + } +} + +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 = ll_round((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(LLUI::getUIImage(image_name)); + setImageSelected(LLUI::getUIImage(selected_name)); +} + +void LLButton::setImageSelected(LLPointer image) +{ + mImageSelected = image; +} + +void LLButton::setImageColor(const LLColor4& c) +{ + mImageColor = c; +} + +void LLButton::setColor(const LLColor4& color) +{ + setImageColor(color); +} + +void LLButton::setImageDisabled(LLPointer image) +{ + mImageDisabled = image; + mDisabledImageColor = mImageColor; + mFadeWhenDisabled = true; +} + +void LLButton::setImageDisabledSelected(LLPointer image) +{ + mImageDisabledSelected = image; + mDisabledImageColor = mImageColor; + mFadeWhenDisabled = true; +} + +void LLButton::setImagePressed(LLPointer image) +{ + mImagePressed = image; +} + +void LLButton::setImageHoverSelected(LLPointer image) +{ + mImageHoverSelected = image; +} + +void LLButton::setImageHoverUnselected(LLPointer image) +{ + mImageHoverUnselected = image; +} + +void LLButton::setImageFlash(LLPointer image) +{ + mImageFlash = image; +} + +void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color) +{ + if (image_name.empty()) + { + mImageOverlay = NULL; + } + else + { + mImageOverlay = LLUI::getUIImage(image_name); + mImageOverlayAlignment = alignment; + mImageOverlayColor = color; + } +} + +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() +{ + if (mCommitOnCaptureLost + && mMouseDownTimer.getStarted()) + { + if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); + + if (mIsToggle) + { + toggleState(); + } + + LLUICtrl::onCommit(); + } + resetMouseDownTimer(); +} + +//------------------------------------------------------------------------- +// Utilities +//------------------------------------------------------------------------- +S32 round_up(S32 grid, S32 value) +{ + S32 mod = value % grid; + + if (mod > 0) + { + // not even multiple + return value + (grid - mod); + } + else + { + return value; + } +} + +void LLButton::addImageAttributeToXML(LLXMLNodePtr node, + const std::string& image_name, + const LLUUID& image_id, + const std::string& xml_tag_name) const +{ + if( !image_name.empty() ) + { + 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); + } +} + + +// static +void LLButton::toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname) +{ + bool floater_vis = LLFloaterReg::toggleInstance(sdname.asString()); + LLButton* button = dynamic_cast(ctrl); + if (button) + button->setToggleState(floater_vis); +} + +// static +// Gets called once +void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname) +{ + LLButton* button = dynamic_cast(ctrl); + if (!button) + return; + // Get the visibility control name for the floater + std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString()); + // Set the button control value (toggle state) to the floater visibility control (Sets the value as well) + button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name)); + // Set the clicked callback to toggle the floater + button->setClickedCallback(boost::bind(&LLFloaterReg::toggleInstance, sdname, LLSD())); +} + +// static +void LLButton::setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname) +{ + LLButton* button = dynamic_cast(ctrl); + if (!button) + return; + // Get the visibility control name for the floater + std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString()); + // Set the button control value (toggle state) to the floater visibility control (Sets the value as well) + button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name)); + // Set the clicked callback to toggle the floater + button->setClickedCallback(boost::bind(&LLDockableFloater::toggleInstance, sdname)); +} + +// static +void LLButton::showHelp(LLUICtrl* ctrl, const LLSD& sdname) +{ + // search back through the button's parents for a panel + // with a help_topic string defined + std::string help_topic; + if (LLUI::getInstance()->mHelpImpl && + ctrl->findHelpTopic(help_topic)) + { + LLUI::getInstance()->mHelpImpl->showTopic(help_topic); + return; // success + } + + // display an error if we can't find a help_topic string. + // fix this by adding a help_topic attribute to the xui file + LLNotificationsUtil::add("UnableToFindHelpTopic"); +} + +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); +} diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 371b716779..fed5cdcc50 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -1,406 +1,406 @@ -/** - * @file llbutton.h - * @brief Header for buttons - * - * $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$ - */ - -#ifndef LL_LLBUTTON_H -#define LL_LLBUTTON_H - -#include "lluuid.h" -#include "llbadgeowner.h" -#include "llcontrol.h" -#include "llflashtimer.h" -#include "lluictrl.h" -#include "v4color.h" -#include "llframetimer.h" -#include "llfontgl.h" -#include "lluiimage.h" -#include "lluistring.h" - -// -// Constants -// - -// PLEASE please use these "constants" when building your own buttons. -extern S32 LLBUTTON_H_PAD; -extern S32 BTN_HEIGHT_SMALL; -extern S32 BTN_HEIGHT; -extern S32 BTN_DROP_SHADOW; - -// -// Helpful functions -// -S32 round_up(S32 grid, S32 value); - - -class LLUICtrlFactory; - -// -// Classes -// - -class LLButton -: public LLUICtrl, public LLBadgeOwner -, public ll::ui::SearchableControl -{ -public: - struct Params - : public LLInitParam::Block - { - // text label - Optional label_selected; - Optional label_shadow; - Optional auto_resize; - Optional use_ellipses; - Optional use_font_color; - - // images - Optional image_unselected, - image_selected, - image_hover_selected, - image_hover_unselected, - image_disabled_selected, - image_disabled, - image_flash, - image_pressed, - image_pressed_selected, - image_overlay; - - Optional image_overlay_alignment; - - // colors - Optional label_color, - label_color_selected, - label_color_disabled, - label_color_disabled_selected, - image_color, - image_color_disabled, - image_overlay_color, - image_overlay_selected_color, - image_overlay_disabled_color, - flash_color; - - // layout - Optional pad_right; - Optional pad_left; - Optional pad_bottom; // under text label - - //image overlay paddings - Optional image_top_pad; - Optional image_bottom_pad; - - /** - * Space between image_overlay and label - */ - Optional imgoverlay_label_space; - - // callbacks - Optional click_callback, // alias -> commit_callback - mouse_down_callback, - mouse_up_callback, - mouse_held_callback; - - // misc - Optional is_toggle, - scale_image, - commit_on_return, - commit_on_capture_lost, - display_pressed_state; - - Optional hover_glow_amount; - Optional held_down_delay; - - Optional use_draw_context_alpha; - - Optional badge; - - Optional handle_right_mouse; - - Optional button_flash_enable; - Optional button_flash_count; - Optional button_flash_rate; - - Params(); - }; - -protected: - friend class LLUICtrlFactory; - LLButton(const Params&); - -public: - - ~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; - 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*/ bool postBuild(); - - virtual void onMouseLeave(S32 x, S32 y, MASK mask); - virtual void onMouseCaptureLost(); - - virtual void onCommit(); - - void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; } - void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; } - void setUseEllipses( bool use_ellipses ) { mUseEllipses = use_ellipses; } - void setUseFontColor( bool use_font_color) { mUseFontColor = use_font_color; } - - - 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 toggleState(); - bool getToggleState() const; - void setToggleState(bool b); - - void setHighlight(bool b); - void setFlashing( bool b, bool force_flashing = false ); - bool getFlashing() const { return mFlashing; } - LLFlashTimer* getFlashTimer() {return mFlashingTimer;} - void setFlashColor(const LLUIColor &color) { mFlashBgColor = color; }; - - void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } - LLFontGL::HAlign getHAlign() const { return mHAlign; } - void setLeftHPad( S32 pad ) { mLeftHPad = pad; } - void setRightHPad( S32 pad ) { mRightHPad = pad; } - - 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); } - - void setImageColor(const std::string& color_control); - void setImageColor(const LLColor4& c); - /*virtual*/ void setColor(const LLColor4& c); - - void setImages(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; } - - void autoResize(); // resize with label of current btn state - void resize(LLUIString label); // resize with label input - void setLabel(const std::string& label); - void setLabel(const LLUIString& label); - 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 setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; } - - void setFont(const LLFontGL *font) - { mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); } - const LLFontGL* getFont() const { return mGLFont; } - const std::string& getText() const { return getCurrentLabel().getString(); } - - S32 getLastDrawCharsCount() const { return mLastDrawCharsCount; } - bool labelIsTruncated() const; - const LLUIString& getCurrentLabel() const; - - void setScaleImage(bool scale) { mScaleImage = scale; } - bool getScaleImage() const { return mScaleImage; } - - void setDropShadowedText(bool b) { mDropShadowedText = b; } - - void setBorderEnabled(bool b) { mBorderEnabled = b; } - - void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; } - - 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 - static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname); - static void setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname); - static void setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname); - static void showHelp(LLUICtrl* ctrl, const LLSD& sdname); - - void setForcePressedState(bool b) { mForcePressedState = b; } - - void setAutoResize(bool auto_resize) { mAutoResize = auto_resize; } - -protected: - LLPointer getImageUnselected() const { return mImageUnselected; } - LLPointer getImageSelected() const { return mImageSelected; } - void getOverlayImageSize(S32& overlay_width, S32& overlay_height); - - LLFrameTimer mMouseDownTimer; - bool mNeedsHighlight; - S32 mButtonFlashCount; - F32 mButtonFlashRate; - - 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; - 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 - S32 mLastDrawCharsCount; - - LLPointer mImageOverlay; - LLFontGL::HAlign mImageOverlayAlignment; - LLUIColor mImageOverlayColor; - LLUIColor mImageOverlaySelectedColor; - LLUIColor mImageOverlayDisabledColor; - - LLPointer mImageUnselected; - LLUIString mUnselectedLabel; - LLUIColor mUnselectedLabelColor; - - LLPointer mImageSelected; - LLUIString mSelectedLabel; - LLUIColor mSelectedLabelColor; - - LLPointer mImageHoverSelected; - - LLPointer mImageHoverUnselected; - - LLPointer mImageDisabled; - LLUIColor mDisabledLabelColor; - - LLPointer mImageDisabledSelected; - LLUIString mDisabledSelectedLabel; - LLUIColor mDisabledSelectedLabelColor; - - LLPointer mImagePressed; - LLPointer mImagePressedSelected; - - /* 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; - - LLUIColor mFlashBgColor; - - LLUIColor mImageColor; - LLUIColor mDisabledImageColor; - - bool mIsToggle; - bool mScaleImage; - - bool mDropShadowedText; - bool mAutoResize; - bool mUseEllipses; - bool mUseFontColor; - bool mBorderEnabled; - bool mFlashing; - - LLFontGL::HAlign mHAlign; - S32 mLeftHPad; - S32 mRightHPad; - S32 mBottomVPad; // under text label - - S32 mImageOverlayTopPad; - S32 mImageOverlayBottomPad; - - bool mUseDrawContextAlpha; - - /* - * Space between image_overlay and label - */ - S32 mImgOverlayLabelSpace; - - F32 mHoverGlowStrength; - F32 mCurGlowStrength; - - bool mCommitOnReturn; - bool mCommitOnCaptureLost; - bool mFadeWhenDisabled; - bool mForcePressedState; - bool mDisplayPressedState; - - LLFrameTimer mFrameTimer; - LLFlashTimer * mFlashingTimer; - bool mForceFlashing; // Stick flashing color even if button is pressed - bool mHandleRightMouse; - -protected: - virtual std::string _getSearchText() const - { - return getLabelUnselected() + getToolTip(); - } -}; - -// Build time optimization, generate once in .cpp file -#ifndef LLBUTTON_CPP -extern template class LLButton* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -#endif // LL_LLBUTTON_H +/** + * @file llbutton.h + * @brief Header for buttons + * + * $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$ + */ + +#ifndef LL_LLBUTTON_H +#define LL_LLBUTTON_H + +#include "lluuid.h" +#include "llbadgeowner.h" +#include "llcontrol.h" +#include "llflashtimer.h" +#include "lluictrl.h" +#include "v4color.h" +#include "llframetimer.h" +#include "llfontgl.h" +#include "lluiimage.h" +#include "lluistring.h" + +// +// Constants +// + +// PLEASE please use these "constants" when building your own buttons. +extern S32 LLBUTTON_H_PAD; +extern S32 BTN_HEIGHT_SMALL; +extern S32 BTN_HEIGHT; +extern S32 BTN_DROP_SHADOW; + +// +// Helpful functions +// +S32 round_up(S32 grid, S32 value); + + +class LLUICtrlFactory; + +// +// Classes +// + +class LLButton +: public LLUICtrl, public LLBadgeOwner +, public ll::ui::SearchableControl +{ +public: + struct Params + : public LLInitParam::Block + { + // text label + Optional label_selected; + Optional label_shadow; + Optional auto_resize; + Optional use_ellipses; + Optional use_font_color; + + // images + Optional image_unselected, + image_selected, + image_hover_selected, + image_hover_unselected, + image_disabled_selected, + image_disabled, + image_flash, + image_pressed, + image_pressed_selected, + image_overlay; + + Optional image_overlay_alignment; + + // colors + Optional label_color, + label_color_selected, + label_color_disabled, + label_color_disabled_selected, + image_color, + image_color_disabled, + image_overlay_color, + image_overlay_selected_color, + image_overlay_disabled_color, + flash_color; + + // layout + Optional pad_right; + Optional pad_left; + Optional pad_bottom; // under text label + + //image overlay paddings + Optional image_top_pad; + Optional image_bottom_pad; + + /** + * Space between image_overlay and label + */ + Optional imgoverlay_label_space; + + // callbacks + Optional click_callback, // alias -> commit_callback + mouse_down_callback, + mouse_up_callback, + mouse_held_callback; + + // misc + Optional is_toggle, + scale_image, + commit_on_return, + commit_on_capture_lost, + display_pressed_state; + + Optional hover_glow_amount; + Optional held_down_delay; + + Optional use_draw_context_alpha; + + Optional badge; + + Optional handle_right_mouse; + + Optional button_flash_enable; + Optional button_flash_count; + Optional button_flash_rate; + + Params(); + }; + +protected: + friend class LLUICtrlFactory; + LLButton(const Params&); + +public: + + ~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; + 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*/ bool postBuild(); + + virtual void onMouseLeave(S32 x, S32 y, MASK mask); + virtual void onMouseCaptureLost(); + + virtual void onCommit(); + + void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; } + void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; } + void setUseEllipses( bool use_ellipses ) { mUseEllipses = use_ellipses; } + void setUseFontColor( bool use_font_color) { mUseFontColor = use_font_color; } + + + 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 toggleState(); + bool getToggleState() const; + void setToggleState(bool b); + + void setHighlight(bool b); + void setFlashing( bool b, bool force_flashing = false ); + bool getFlashing() const { return mFlashing; } + LLFlashTimer* getFlashTimer() {return mFlashingTimer;} + void setFlashColor(const LLUIColor &color) { mFlashBgColor = color; }; + + void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } + LLFontGL::HAlign getHAlign() const { return mHAlign; } + void setLeftHPad( S32 pad ) { mLeftHPad = pad; } + void setRightHPad( S32 pad ) { mRightHPad = pad; } + + 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); } + + void setImageColor(const std::string& color_control); + void setImageColor(const LLColor4& c); + /*virtual*/ void setColor(const LLColor4& c); + + void setImages(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; } + + void autoResize(); // resize with label of current btn state + void resize(LLUIString label); // resize with label input + void setLabel(const std::string& label); + void setLabel(const LLUIString& label); + 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 setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; } + + void setFont(const LLFontGL *font) + { mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); } + const LLFontGL* getFont() const { return mGLFont; } + const std::string& getText() const { return getCurrentLabel().getString(); } + + S32 getLastDrawCharsCount() const { return mLastDrawCharsCount; } + bool labelIsTruncated() const; + const LLUIString& getCurrentLabel() const; + + void setScaleImage(bool scale) { mScaleImage = scale; } + bool getScaleImage() const { return mScaleImage; } + + void setDropShadowedText(bool b) { mDropShadowedText = b; } + + void setBorderEnabled(bool b) { mBorderEnabled = b; } + + void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; } + + 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 + static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname); + static void setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname); + static void setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname); + static void showHelp(LLUICtrl* ctrl, const LLSD& sdname); + + void setForcePressedState(bool b) { mForcePressedState = b; } + + void setAutoResize(bool auto_resize) { mAutoResize = auto_resize; } + +protected: + LLPointer getImageUnselected() const { return mImageUnselected; } + LLPointer getImageSelected() const { return mImageSelected; } + void getOverlayImageSize(S32& overlay_width, S32& overlay_height); + + LLFrameTimer mMouseDownTimer; + bool mNeedsHighlight; + S32 mButtonFlashCount; + F32 mButtonFlashRate; + + 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; + 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 + S32 mLastDrawCharsCount; + + LLPointer mImageOverlay; + LLFontGL::HAlign mImageOverlayAlignment; + LLUIColor mImageOverlayColor; + LLUIColor mImageOverlaySelectedColor; + LLUIColor mImageOverlayDisabledColor; + + LLPointer mImageUnselected; + LLUIString mUnselectedLabel; + LLUIColor mUnselectedLabelColor; + + LLPointer mImageSelected; + LLUIString mSelectedLabel; + LLUIColor mSelectedLabelColor; + + LLPointer mImageHoverSelected; + + LLPointer mImageHoverUnselected; + + LLPointer mImageDisabled; + LLUIColor mDisabledLabelColor; + + LLPointer mImageDisabledSelected; + LLUIString mDisabledSelectedLabel; + LLUIColor mDisabledSelectedLabelColor; + + LLPointer mImagePressed; + LLPointer mImagePressedSelected; + + /* 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; + + LLUIColor mFlashBgColor; + + LLUIColor mImageColor; + LLUIColor mDisabledImageColor; + + bool mIsToggle; + bool mScaleImage; + + bool mDropShadowedText; + bool mAutoResize; + bool mUseEllipses; + bool mUseFontColor; + bool mBorderEnabled; + bool mFlashing; + + LLFontGL::HAlign mHAlign; + S32 mLeftHPad; + S32 mRightHPad; + S32 mBottomVPad; // under text label + + S32 mImageOverlayTopPad; + S32 mImageOverlayBottomPad; + + bool mUseDrawContextAlpha; + + /* + * Space between image_overlay and label + */ + S32 mImgOverlayLabelSpace; + + F32 mHoverGlowStrength; + F32 mCurGlowStrength; + + bool mCommitOnReturn; + bool mCommitOnCaptureLost; + bool mFadeWhenDisabled; + bool mForcePressedState; + bool mDisplayPressedState; + + LLFrameTimer mFrameTimer; + LLFlashTimer * mFlashingTimer; + bool mForceFlashing; // Stick flashing color even if button is pressed + bool mHandleRightMouse; + +protected: + virtual std::string _getSearchText() const + { + return getLabelUnselected() + getToolTip(); + } +}; + +// Build time optimization, generate once in .cpp file +#ifndef LLBUTTON_CPP +extern template class LLButton* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +#endif // LL_LLBUTTON_H diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h index 318e7e327e..5f75ed2f8d 100644 --- a/indra/llui/llchat.h +++ b/indra/llui/llchat.h @@ -1,112 +1,112 @@ -/** - * @file llchat.h - * @author James Cook - * @brief Chat constants and data structures. - * - * $LicenseInfo:firstyear=2006&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_LLCHAT_H -#define LL_LLCHAT_H - -#include "lluuid.h" -#include "v3math.h" - -// enumerations used by the chat system -typedef enum e_chat_source_type -{ - CHAT_SOURCE_SYSTEM = 0, - CHAT_SOURCE_AGENT = 1, - CHAT_SOURCE_OBJECT = 2, - CHAT_SOURCE_TELEPORT = 3, - CHAT_SOURCE_UNKNOWN = 4, - CHAT_SOURCE_REGION = 5, -} EChatSourceType; - -typedef enum e_chat_type -{ - CHAT_TYPE_WHISPER = 0, - CHAT_TYPE_NORMAL = 1, - CHAT_TYPE_SHOUT = 2, - CHAT_TYPE_START = 4, - CHAT_TYPE_STOP = 5, - CHAT_TYPE_DEBUG_MSG = 6, - CHAT_TYPE_REGION = 7, - CHAT_TYPE_OWNER = 8, - CHAT_TYPE_DIRECT = 9 // From llRegionSayTo() -} EChatType; - -typedef enum e_chat_audible_level -{ - CHAT_AUDIBLE_NOT = -1, - CHAT_AUDIBLE_BARELY = 0, - CHAT_AUDIBLE_FULLY = 1 -} EChatAudible; - -typedef enum e_chat_style -{ - CHAT_STYLE_NORMAL, - CHAT_STYLE_IRC, - CHAT_STYLE_HISTORY, - CHAT_STYLE_TELEPORT_SEP -}EChatStyle; - -// A piece of chat -class LLChat -{ -public: - LLChat(const std::string& text = std::string()) - : mText(text), - mFromName(), - mFromID(), - mNotifId(), - mOwnerID(), - mSourceType(CHAT_SOURCE_AGENT), - mChatType(CHAT_TYPE_NORMAL), - mAudible(CHAT_AUDIBLE_FULLY), - mMuted(false), - mTime(0.0), - mTimeStr(), - mPosAgent(), - mURL(), - mChatStyle(CHAT_STYLE_NORMAL), - mSessionID() - { } - - std::string mText; // UTF-8 line of text - std::string mFromName; // agent or object name - LLUUID mFromID; // agent id or object id - LLUUID mNotifId; - LLUUID mOwnerID; - EChatSourceType mSourceType; - EChatType mChatType; - EChatAudible mAudible; - bool mMuted; // pass muted chat to maintain list of chatters - F64 mTime; // viewer only, seconds from viewer start - std::string mTimeStr; - LLVector3 mPosAgent; - std::string mURL; - EChatStyle mChatStyle; - LLUUID mSessionID; -}; - -#endif +/** + * @file llchat.h + * @author James Cook + * @brief Chat constants and data structures. + * + * $LicenseInfo:firstyear=2006&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_LLCHAT_H +#define LL_LLCHAT_H + +#include "lluuid.h" +#include "v3math.h" + +// enumerations used by the chat system +typedef enum e_chat_source_type +{ + CHAT_SOURCE_SYSTEM = 0, + CHAT_SOURCE_AGENT = 1, + CHAT_SOURCE_OBJECT = 2, + CHAT_SOURCE_TELEPORT = 3, + CHAT_SOURCE_UNKNOWN = 4, + CHAT_SOURCE_REGION = 5, +} EChatSourceType; + +typedef enum e_chat_type +{ + CHAT_TYPE_WHISPER = 0, + CHAT_TYPE_NORMAL = 1, + CHAT_TYPE_SHOUT = 2, + CHAT_TYPE_START = 4, + CHAT_TYPE_STOP = 5, + CHAT_TYPE_DEBUG_MSG = 6, + CHAT_TYPE_REGION = 7, + CHAT_TYPE_OWNER = 8, + CHAT_TYPE_DIRECT = 9 // From llRegionSayTo() +} EChatType; + +typedef enum e_chat_audible_level +{ + CHAT_AUDIBLE_NOT = -1, + CHAT_AUDIBLE_BARELY = 0, + CHAT_AUDIBLE_FULLY = 1 +} EChatAudible; + +typedef enum e_chat_style +{ + CHAT_STYLE_NORMAL, + CHAT_STYLE_IRC, + CHAT_STYLE_HISTORY, + CHAT_STYLE_TELEPORT_SEP +}EChatStyle; + +// A piece of chat +class LLChat +{ +public: + LLChat(const std::string& text = std::string()) + : mText(text), + mFromName(), + mFromID(), + mNotifId(), + mOwnerID(), + mSourceType(CHAT_SOURCE_AGENT), + mChatType(CHAT_TYPE_NORMAL), + mAudible(CHAT_AUDIBLE_FULLY), + mMuted(false), + mTime(0.0), + mTimeStr(), + mPosAgent(), + mURL(), + mChatStyle(CHAT_STYLE_NORMAL), + mSessionID() + { } + + std::string mText; // UTF-8 line of text + std::string mFromName; // agent or object name + LLUUID mFromID; // agent id or object id + LLUUID mNotifId; + LLUUID mOwnerID; + EChatSourceType mSourceType; + EChatType mChatType; + EChatAudible mAudible; + bool mMuted; // pass muted chat to maintain list of chatters + F64 mTime; // viewer only, seconds from viewer start + std::string mTimeStr; + LLVector3 mPosAgent; + std::string mURL; + EChatStyle mChatStyle; + LLUUID mSessionID; +}; + +#endif diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp index 3aa65355e5..da5afd0386 100644 --- a/indra/llui/llchatentry.cpp +++ b/indra/llui/llchatentry.cpp @@ -1,251 +1,251 @@ -/** - * @file llchatentry.cpp - * @brief LLChatEntry implementation - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, 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 "llscrollcontainer.h" - -#include "llchatentry.h" - -static LLDefaultChildRegistry::Register r("chat_editor"); - -LLChatEntry::Params::Params() -: has_history("has_history", true), - is_expandable("is_expandable", false), - expand_lines_count("expand_lines_count", 1) -{} - -LLChatEntry::LLChatEntry(const Params& p) -: LLTextEditor(p), - mTextExpandedSignal(NULL), - mHasHistory(p.has_history), - mIsExpandable(p.is_expandable), - mExpandLinesCount(p.expand_lines_count), - mPrevLinesCount(0), - mSingleLineMode(false), - mPrevExpandedLineCount(S32_MAX) -{ - // Initialize current history line iterator - mCurrentHistoryLine = mLineHistory.begin(); - - mAutoIndent = false; - keepSelectionOnReturn(true); -} - -LLChatEntry::~LLChatEntry() -{ - delete mTextExpandedSignal; -} - -void LLChatEntry::draw() -{ - if(mIsExpandable) - { - reflow(); - expandText(); - } - LLTextEditor::draw(); -} - -void LLChatEntry::onCommit() -{ - updateHistory(); - LLTextEditor::onCommit(); -} - -boost::signals2::connection LLChatEntry::setTextExpandedCallback(const commit_signal_t::slot_type& cb) -{ - if (!mTextExpandedSignal) - { - mTextExpandedSignal = new commit_signal_t(); - } - return mTextExpandedSignal->connect(cb); -} - -void LLChatEntry::expandText() -{ - S32 line_count = mSingleLineMode ? 1 : mExpandLinesCount; - - int visible_lines_count = llabs(getVisibleLines(true).first - getVisibleLines(true).second); - bool can_changed = getLineCount() <= line_count || line_count < mPrevExpandedLineCount ; - mPrevExpandedLineCount = line_count; - - // true if pasted text has more lines than expand height limit and expand limit is not reached yet - bool text_pasted = (getLineCount() > line_count) && (visible_lines_count < line_count); - - if (mIsExpandable && (can_changed || text_pasted || mSingleLineMode) && getLineCount() != mPrevLinesCount) - { - int lines_height = 0; - if (text_pasted) - { - // text is pasted and now mLineInfoList.size() > mExpandLineCounts and mLineInfoList is not empty, - // so lines_height is the sum of the last 'expanded_line_count' lines height - lines_height = (mLineInfoList.end() - line_count)->mRect.mTop - mLineInfoList.back().mRect.mBottom; - } - else - { - lines_height = mLineInfoList.begin()->mRect.mTop - mLineInfoList.back().mRect.mBottom; - } - - int height = mVPad * 2 + lines_height; - - LLRect doc_rect = getRect(); - doc_rect.setOriginAndSize(doc_rect.mLeft, doc_rect.mBottom, doc_rect.getWidth(), height); - setShape(doc_rect); - - mPrevLinesCount = getLineCount(); - - if (mTextExpandedSignal) - { - (*mTextExpandedSignal)(this, LLSD() ); - } - - needsReflow(); - } -} - -// line history support -void LLChatEntry::updateHistory() -{ - // On history enabled, remember committed line and - // reset current history line number. - // Be sure only to remember lines that are not empty and that are - // different from the last on the list. - if (mHasHistory && getLength()) - { - // Add text to history, ignoring duplicates - if (mLineHistory.empty() || getText() != mLineHistory.back()) - { - mLineHistory.push_back(getText()); - } - - mCurrentHistoryLine = mLineHistory.end(); - } -} - -void LLChatEntry::beforeValueChange() -{ - if(this->getLength() == 0 && !mLabel.empty()) - { - this->clearSegments(); - } -} - -void LLChatEntry::onValueChange(S32 start, S32 end) -{ - //Internally resetLabel() must meet a condition before it can reset the label - resetLabel(); -} - -bool LLChatEntry::useLabel() const -{ - return !getLength() && !mLabel.empty(); -} - -void LLChatEntry::onFocusReceived() -{ - LLUICtrl::onFocusReceived(); - updateAllowingLanguageInput(); -} - -void LLChatEntry::onFocusLost() -{ - LLTextEditor::focusLostHelper(); - LLUICtrl::onFocusLost(); -} - -bool LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) -{ - bool handled = false; - - LLTextEditor::handleSpecialKey(key, mask); - - switch(key) - { - case KEY_RETURN: - if (MASK_NONE == mask) - { - needsReflow(); - } - break; - - case KEY_UP: - if (mHasHistory && MASK_CONTROL == mask) - { - if (!mLineHistory.empty() && mCurrentHistoryLine > mLineHistory.begin()) - { - setText(*(--mCurrentHistoryLine)); - endOfDoc(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - case KEY_DOWN: - if (mHasHistory && MASK_CONTROL == mask) - { - if (!mLineHistory.empty() && mCurrentHistoryLine < (mLineHistory.end() - 1) ) - { - setText(*(++mCurrentHistoryLine)); - endOfDoc(); - } - else if (!mLineHistory.empty() && mCurrentHistoryLine == (mLineHistory.end() - 1) ) - { - mCurrentHistoryLine++; - std::string empty(""); - setText(empty); - needsReflow(); - endOfDoc(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - default: - break; - } - - return handled; -} - -void LLChatEntry::enableSingleLineMode(bool single_line_mode) -{ - if (mScroller) - { - mScroller->setSize(single_line_mode ? 0 : -1); - } - - mSingleLineMode = single_line_mode; - mPrevLinesCount = -1; - setWordWrap(!single_line_mode); -} +/** + * @file llchatentry.cpp + * @brief LLChatEntry implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llscrollcontainer.h" + +#include "llchatentry.h" + +static LLDefaultChildRegistry::Register r("chat_editor"); + +LLChatEntry::Params::Params() +: has_history("has_history", true), + is_expandable("is_expandable", false), + expand_lines_count("expand_lines_count", 1) +{} + +LLChatEntry::LLChatEntry(const Params& p) +: LLTextEditor(p), + mTextExpandedSignal(NULL), + mHasHistory(p.has_history), + mIsExpandable(p.is_expandable), + mExpandLinesCount(p.expand_lines_count), + mPrevLinesCount(0), + mSingleLineMode(false), + mPrevExpandedLineCount(S32_MAX) +{ + // Initialize current history line iterator + mCurrentHistoryLine = mLineHistory.begin(); + + mAutoIndent = false; + keepSelectionOnReturn(true); +} + +LLChatEntry::~LLChatEntry() +{ + delete mTextExpandedSignal; +} + +void LLChatEntry::draw() +{ + if(mIsExpandable) + { + reflow(); + expandText(); + } + LLTextEditor::draw(); +} + +void LLChatEntry::onCommit() +{ + updateHistory(); + LLTextEditor::onCommit(); +} + +boost::signals2::connection LLChatEntry::setTextExpandedCallback(const commit_signal_t::slot_type& cb) +{ + if (!mTextExpandedSignal) + { + mTextExpandedSignal = new commit_signal_t(); + } + return mTextExpandedSignal->connect(cb); +} + +void LLChatEntry::expandText() +{ + S32 line_count = mSingleLineMode ? 1 : mExpandLinesCount; + + int visible_lines_count = llabs(getVisibleLines(true).first - getVisibleLines(true).second); + bool can_changed = getLineCount() <= line_count || line_count < mPrevExpandedLineCount ; + mPrevExpandedLineCount = line_count; + + // true if pasted text has more lines than expand height limit and expand limit is not reached yet + bool text_pasted = (getLineCount() > line_count) && (visible_lines_count < line_count); + + if (mIsExpandable && (can_changed || text_pasted || mSingleLineMode) && getLineCount() != mPrevLinesCount) + { + int lines_height = 0; + if (text_pasted) + { + // text is pasted and now mLineInfoList.size() > mExpandLineCounts and mLineInfoList is not empty, + // so lines_height is the sum of the last 'expanded_line_count' lines height + lines_height = (mLineInfoList.end() - line_count)->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + else + { + lines_height = mLineInfoList.begin()->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + + int height = mVPad * 2 + lines_height; + + LLRect doc_rect = getRect(); + doc_rect.setOriginAndSize(doc_rect.mLeft, doc_rect.mBottom, doc_rect.getWidth(), height); + setShape(doc_rect); + + mPrevLinesCount = getLineCount(); + + if (mTextExpandedSignal) + { + (*mTextExpandedSignal)(this, LLSD() ); + } + + needsReflow(); + } +} + +// line history support +void LLChatEntry::updateHistory() +{ + // On history enabled, remember committed line and + // reset current history line number. + // Be sure only to remember lines that are not empty and that are + // different from the last on the list. + if (mHasHistory && getLength()) + { + // Add text to history, ignoring duplicates + if (mLineHistory.empty() || getText() != mLineHistory.back()) + { + mLineHistory.push_back(getText()); + } + + mCurrentHistoryLine = mLineHistory.end(); + } +} + +void LLChatEntry::beforeValueChange() +{ + if(this->getLength() == 0 && !mLabel.empty()) + { + this->clearSegments(); + } +} + +void LLChatEntry::onValueChange(S32 start, S32 end) +{ + //Internally resetLabel() must meet a condition before it can reset the label + resetLabel(); +} + +bool LLChatEntry::useLabel() const +{ + return !getLength() && !mLabel.empty(); +} + +void LLChatEntry::onFocusReceived() +{ + LLUICtrl::onFocusReceived(); + updateAllowingLanguageInput(); +} + +void LLChatEntry::onFocusLost() +{ + LLTextEditor::focusLostHelper(); + LLUICtrl::onFocusLost(); +} + +bool LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) +{ + bool handled = false; + + LLTextEditor::handleSpecialKey(key, mask); + + switch(key) + { + case KEY_RETURN: + if (MASK_NONE == mask) + { + needsReflow(); + } + break; + + case KEY_UP: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && mCurrentHistoryLine > mLineHistory.begin()) + { + setText(*(--mCurrentHistoryLine)); + endOfDoc(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + case KEY_DOWN: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && mCurrentHistoryLine < (mLineHistory.end() - 1) ) + { + setText(*(++mCurrentHistoryLine)); + endOfDoc(); + } + else if (!mLineHistory.empty() && mCurrentHistoryLine == (mLineHistory.end() - 1) ) + { + mCurrentHistoryLine++; + std::string empty(""); + setText(empty); + needsReflow(); + endOfDoc(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + default: + break; + } + + return handled; +} + +void LLChatEntry::enableSingleLineMode(bool single_line_mode) +{ + if (mScroller) + { + mScroller->setSize(single_line_mode ? 0 : -1); + } + + mSingleLineMode = single_line_mode; + mPrevLinesCount = -1; + setWordWrap(!single_line_mode); +} diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h index 5fc336c8cc..5621ede1e7 100644 --- a/indra/llui/llchatentry.h +++ b/indra/llui/llchatentry.h @@ -1,106 +1,106 @@ -/** - * @file llchatentry.h - * @author Paul Guslisty - * @brief Text editor widget which is used for user input - * - * Features: - * Optional line history so previous entries can be recalled by CTRL UP/DOWN - * Optional auto-resize behavior on input chat field - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, 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 LLCHATENTRY_H_ -#define LLCHATENTRY_H_ - -#include "lltexteditor.h" - -class LLChatEntry : public LLTextEditor -{ -public: - - struct Params : public LLInitParam::Block - { - Optional has_history, - is_expandable; - - Optional expand_lines_count; - - Params(); - }; - - virtual ~LLChatEntry(); - -protected: - - friend class LLUICtrlFactory; - LLChatEntry(const Params& p); - /*virtual*/ void beforeValueChange(); - /*virtual*/ void onValueChange(S32 start, S32 end); - /*virtual*/ bool useLabel() const; - -public: - - virtual void draw(); - virtual void onCommit(); - /*virtual*/ void onFocusReceived(); - /*virtual*/ void onFocusLost(); - - void enableSingleLineMode(bool single_line_mode); - boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb); - -private: - - /** - * Implements auto-resize behavior. - * When user's typing reaches the right edge of the chat field - * the chat field expands vertically by one line. The bottom of - * the chat field remains bottom-justified. The chat field does - * not expand beyond mExpandLinesCount. - */ - void expandText(); - - /** - * Implements line history so previous entries can be recalled by CTRL UP/DOWN - */ - void updateHistory(); - - bool handleSpecialKey(const KEY key, const MASK mask); - - - // Fired when text height expanded to mExpandLinesCount - commit_signal_t* mTextExpandedSignal; - - // line history support: - typedef std::vector line_history_t; - line_history_t::iterator mCurrentHistoryLine; // currently browsed history line - line_history_t mLineHistory; // line history storage - bool mHasHistory; // flag for enabled/disabled line history - bool mIsExpandable; - bool mSingleLineMode; - - S32 mExpandLinesCount; - S32 mPrevLinesCount; - S32 mPrevExpandedLineCount; -}; - -#endif /* LLCHATENTRY_H_ */ +/** + * @file llchatentry.h + * @author Paul Guslisty + * @brief Text editor widget which is used for user input + * + * Features: + * Optional line history so previous entries can be recalled by CTRL UP/DOWN + * Optional auto-resize behavior on input chat field + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 LLCHATENTRY_H_ +#define LLCHATENTRY_H_ + +#include "lltexteditor.h" + +class LLChatEntry : public LLTextEditor +{ +public: + + struct Params : public LLInitParam::Block + { + Optional has_history, + is_expandable; + + Optional expand_lines_count; + + Params(); + }; + + virtual ~LLChatEntry(); + +protected: + + friend class LLUICtrlFactory; + LLChatEntry(const Params& p); + /*virtual*/ void beforeValueChange(); + /*virtual*/ void onValueChange(S32 start, S32 end); + /*virtual*/ bool useLabel() const; + +public: + + virtual void draw(); + virtual void onCommit(); + /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost(); + + void enableSingleLineMode(bool single_line_mode); + boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb); + +private: + + /** + * Implements auto-resize behavior. + * When user's typing reaches the right edge of the chat field + * the chat field expands vertically by one line. The bottom of + * the chat field remains bottom-justified. The chat field does + * not expand beyond mExpandLinesCount. + */ + void expandText(); + + /** + * Implements line history so previous entries can be recalled by CTRL UP/DOWN + */ + void updateHistory(); + + bool handleSpecialKey(const KEY key, const MASK mask); + + + // Fired when text height expanded to mExpandLinesCount + commit_signal_t* mTextExpandedSignal; + + // line history support: + typedef std::vector line_history_t; + line_history_t::iterator mCurrentHistoryLine; // currently browsed history line + line_history_t mLineHistory; // line history storage + bool mHasHistory; // flag for enabled/disabled line history + bool mIsExpandable; + bool mSingleLineMode; + + S32 mExpandLinesCount; + S32 mPrevLinesCount; + S32 mPrevExpandedLineCount; +}; + +#endif /* LLCHATENTRY_H_ */ diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 8578059fab..3bcf0a6517 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -1,304 +1,304 @@ -/** - * @file llcheckboxctrl.cpp - * @brief LLCheckBoxCtrl base class - * - * $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$ - */ - -// The mutants are coming! -#include "linden_common.h" - -#define LLCHECKBOXCTRL_CPP -#include "llcheckboxctrl.h" - -#include "llgl.h" -#include "llui.h" -#include "lluiconstants.h" -#include "lluictrlfactory.h" -#include "llcontrol.h" - -#include "llstring.h" -#include "llfontgl.h" -#include "lltextbox.h" -#include "llkeyboard.h" - -static LLDefaultChildRegistry::Register r("check_box"); - -// Compiler optimization, generate extern template -template class LLCheckBoxCtrl* LLView::getChild( - const std::string& name, bool recurse) const; - -void LLCheckBoxCtrl::WordWrap::declareValues() -{ - declare("none", EWordWrap::WRAP_NONE); - declare("down", EWordWrap::WRAP_DOWN); - declare("up", EWordWrap::WRAP_UP); -} - -LLCheckBoxCtrl::Params::Params() -: initial_value("initial_value", false), - label_text("label_text"), - check_button("check_button"), - word_wrap("word_wrap", EWordWrap::WRAP_NONE), - radio_style("radio_style") -{} - - -LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) -: LLUICtrl(p), - mTextEnabledColor(p.label_text.text_color()), - mTextDisabledColor(p.label_text.text_readonly_color()), - mFont(p.font()), - mWordWrap(p.word_wrap) -{ - mViewModel->setValue(LLSD(p.initial_value)); - mViewModel->resetDirty(); - static LLUICachedControl llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0); - static LLUICachedControl llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0); - static LLUICachedControl llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0); - - // must be big enough to hold all children - setUseBoundingRect(true); - - // *HACK Get rid of this with SL-55508... - // this allows blank check boxes and radio boxes for now - std::string local_label = p.label; - if(local_label.empty()) - { - local_label = " "; - } - - LLTextBox::Params tbparams = p.label_text; - tbparams.initial_value(local_label); - if (p.font.isProvided()) - { - tbparams.font(p.font); - } - - mLabel = LLUICtrlFactory::create(tbparams); - if (mWordWrap != WRAP_NONE) - { - // Not setWordWrap(mWordWrap != WRAP_NONE) because there might be some old lurking code that sets it manually - mLabel->setWordWrap(true); - S32 new_width = getRect().getWidth() - p.check_button.rect().getWidth() - llcheckboxctrl_hpad; - LLRect label_rect = mLabel->getRect(); - label_rect.mRight = label_rect.mLeft + new_width; - mLabel->setRect(label_rect); - } - mLabel->reshapeToFitText(); - - LLRect label_rect = mLabel->getRect(); - if (mLabel->getLineCount() > 1) - { - if (mWordWrap == WRAP_DOWN) - { - // reshapeToFitText uses LLView::reshape() which always reshapes - // from bottom to top, but we want to extend the bottom - // Note: might be better idea to use getRect().mTop of LLCheckBoxCtrl (+pad) as top point of new rect - S32 delta = ll_round((F32)mLabel->getFont()->getLineHeight() * mLabel->getLineSpacingMult()) - label_rect.getHeight(); - label_rect.translate(0, delta); - mLabel->setRect(label_rect); - } - // else - // WRAP_UP is essentially done by reshapeToFitText() (extends from bottom to top) - // howhever it doesn't respect rect of checkbox - // todo: this should be fixed, but there are at least couple checkboxes that use this feature as is. - } - - addChild(mLabel); - - // Button - // Note: button cover the label by extending all the way to the right and down. - LLRect btn_rect = p.check_button.rect(); - btn_rect.setOriginAndSize( - btn_rect.mLeft, - llmin(btn_rect.mBottom, label_rect.mBottom), - llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft), - llmax(label_rect.getHeight(), btn_rect.mTop)); - std::string active_true_id, active_false_id; - std::string inactive_true_id, inactive_false_id; - - LLButton::Params params = p.check_button; - params.rect(btn_rect); - //params.control_name(p.control_name); - params.click_callback.function(boost::bind(&LLCheckBoxCtrl::onCommit, this)); - params.commit_on_return(false); - // Checkboxes only allow boolean initial values, but buttons can - // take any LLSD. - params.initial_value(LLSD(p.initial_value)); - params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); - - mButton = LLUICtrlFactory::create(params); - addChild(mButton); -} - -LLCheckBoxCtrl::~LLCheckBoxCtrl() -{ - // Children all cleaned up by default view destructor. -} - -void LLCheckBoxCtrl::onCommit() -{ - if( getEnabled() ) - { - setTentative(false); - setControlValue(getValue()); - LLUICtrl::onCommit(); - } -} - -void LLCheckBoxCtrl::setEnabled(bool b) -{ - LLView::setEnabled(b); - - if (b) - { - mLabel->setColor( mTextEnabledColor.get() ); - } - else - { - mLabel->setColor( mTextDisabledColor.get() ); - } -} - -void LLCheckBoxCtrl::clear() -{ - setValue( false ); -} - -void LLCheckBoxCtrl::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLRect rect = getRect(); - S32 delta_width = width - rect.getWidth(); - S32 delta_height = height - rect.getHeight(); - - if (delta_width || delta_height) - { - // adjust our rectangle - rect.mRight = getRect().mLeft + width; - rect.mTop = getRect().mBottom + height; - setRect(rect); - } - - // reshapeToFitText reshapes label to minimal size according to last bounding box - // it will work fine in case of decrease of space, but if we get more space or text - // becomes longer, label will fail to grow so reinit label's dimentions. - - LLRect label_rect = mLabel->getRect(); - S32 new_width = rect.getWidth() - label_rect.mLeft; - mLabel->reshape(new_width, label_rect.getHeight(), true); - - S32 label_top = label_rect.mTop; - mLabel->reshapeToFitText(true); - - label_rect = mLabel->getRect(); - if (label_top != label_rect.mTop && mWordWrap == WRAP_DOWN) - { - // reshapeToFitText uses LLView::reshape() which always reshapes - // from bottom to top, but we want to extend the bottom so - // reposition control - S32 delta = label_top - label_rect.mTop; - label_rect.translate(0, delta); - mLabel->setRect(label_rect); - } - - // Button - // Note: button cover the label by extending all the way to the right and down. - LLRect btn_rect = mButton->getRect(); - btn_rect.setOriginAndSize( - btn_rect.mLeft, - llmin(btn_rect.mBottom, label_rect.mBottom), - llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft), - llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight())); - mButton->setShape(btn_rect); - - updateBoundingRect(); -} - -//virtual -void LLCheckBoxCtrl::setValue(const LLSD& value ) -{ - mButton->setValue( value ); -} - -//virtual -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 ); - reshape(getRect().getWidth(), getRect().getHeight(), false); -} - -std::string LLCheckBoxCtrl::getLabel() const -{ - return mLabel->getText(); -} - -bool LLCheckBoxCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - bool res = mLabel->setTextArg(key, text); - reshape(getRect().getWidth(), getRect().getHeight(), false); - return res; -} - -// virtual -void LLCheckBoxCtrl::setControlName(const std::string& control_name, LLView* context) -{ - mButton->setControlName(control_name, context); -} - - -// virtual Returns true if the user has modified this control. -bool LLCheckBoxCtrl::isDirty() const -{ - if ( mButton ) - { - return mButton->isDirty(); - } - return false; // Shouldn't get here -} - - -// virtual Clear dirty state -void LLCheckBoxCtrl::resetDirty() -{ - if ( mButton ) - { - mButton->resetDirty(); - } -} +/** + * @file llcheckboxctrl.cpp + * @brief LLCheckBoxCtrl base class + * + * $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$ + */ + +// The mutants are coming! +#include "linden_common.h" + +#define LLCHECKBOXCTRL_CPP +#include "llcheckboxctrl.h" + +#include "llgl.h" +#include "llui.h" +#include "lluiconstants.h" +#include "lluictrlfactory.h" +#include "llcontrol.h" + +#include "llstring.h" +#include "llfontgl.h" +#include "lltextbox.h" +#include "llkeyboard.h" + +static LLDefaultChildRegistry::Register r("check_box"); + +// Compiler optimization, generate extern template +template class LLCheckBoxCtrl* LLView::getChild( + const std::string& name, bool recurse) const; + +void LLCheckBoxCtrl::WordWrap::declareValues() +{ + declare("none", EWordWrap::WRAP_NONE); + declare("down", EWordWrap::WRAP_DOWN); + declare("up", EWordWrap::WRAP_UP); +} + +LLCheckBoxCtrl::Params::Params() +: initial_value("initial_value", false), + label_text("label_text"), + check_button("check_button"), + word_wrap("word_wrap", EWordWrap::WRAP_NONE), + radio_style("radio_style") +{} + + +LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) +: LLUICtrl(p), + mTextEnabledColor(p.label_text.text_color()), + mTextDisabledColor(p.label_text.text_readonly_color()), + mFont(p.font()), + mWordWrap(p.word_wrap) +{ + mViewModel->setValue(LLSD(p.initial_value)); + mViewModel->resetDirty(); + static LLUICachedControl llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0); + static LLUICachedControl llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0); + static LLUICachedControl llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0); + + // must be big enough to hold all children + setUseBoundingRect(true); + + // *HACK Get rid of this with SL-55508... + // this allows blank check boxes and radio boxes for now + std::string local_label = p.label; + if(local_label.empty()) + { + local_label = " "; + } + + LLTextBox::Params tbparams = p.label_text; + tbparams.initial_value(local_label); + if (p.font.isProvided()) + { + tbparams.font(p.font); + } + + mLabel = LLUICtrlFactory::create(tbparams); + if (mWordWrap != WRAP_NONE) + { + // Not setWordWrap(mWordWrap != WRAP_NONE) because there might be some old lurking code that sets it manually + mLabel->setWordWrap(true); + S32 new_width = getRect().getWidth() - p.check_button.rect().getWidth() - llcheckboxctrl_hpad; + LLRect label_rect = mLabel->getRect(); + label_rect.mRight = label_rect.mLeft + new_width; + mLabel->setRect(label_rect); + } + mLabel->reshapeToFitText(); + + LLRect label_rect = mLabel->getRect(); + if (mLabel->getLineCount() > 1) + { + if (mWordWrap == WRAP_DOWN) + { + // reshapeToFitText uses LLView::reshape() which always reshapes + // from bottom to top, but we want to extend the bottom + // Note: might be better idea to use getRect().mTop of LLCheckBoxCtrl (+pad) as top point of new rect + S32 delta = ll_round((F32)mLabel->getFont()->getLineHeight() * mLabel->getLineSpacingMult()) - label_rect.getHeight(); + label_rect.translate(0, delta); + mLabel->setRect(label_rect); + } + // else + // WRAP_UP is essentially done by reshapeToFitText() (extends from bottom to top) + // howhever it doesn't respect rect of checkbox + // todo: this should be fixed, but there are at least couple checkboxes that use this feature as is. + } + + addChild(mLabel); + + // Button + // Note: button cover the label by extending all the way to the right and down. + LLRect btn_rect = p.check_button.rect(); + btn_rect.setOriginAndSize( + btn_rect.mLeft, + llmin(btn_rect.mBottom, label_rect.mBottom), + llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft), + llmax(label_rect.getHeight(), btn_rect.mTop)); + std::string active_true_id, active_false_id; + std::string inactive_true_id, inactive_false_id; + + LLButton::Params params = p.check_button; + params.rect(btn_rect); + //params.control_name(p.control_name); + params.click_callback.function(boost::bind(&LLCheckBoxCtrl::onCommit, this)); + params.commit_on_return(false); + // Checkboxes only allow boolean initial values, but buttons can + // take any LLSD. + params.initial_value(LLSD(p.initial_value)); + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + + mButton = LLUICtrlFactory::create(params); + addChild(mButton); +} + +LLCheckBoxCtrl::~LLCheckBoxCtrl() +{ + // Children all cleaned up by default view destructor. +} + +void LLCheckBoxCtrl::onCommit() +{ + if( getEnabled() ) + { + setTentative(false); + setControlValue(getValue()); + LLUICtrl::onCommit(); + } +} + +void LLCheckBoxCtrl::setEnabled(bool b) +{ + LLView::setEnabled(b); + + if (b) + { + mLabel->setColor( mTextEnabledColor.get() ); + } + else + { + mLabel->setColor( mTextDisabledColor.get() ); + } +} + +void LLCheckBoxCtrl::clear() +{ + setValue( false ); +} + +void LLCheckBoxCtrl::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLRect rect = getRect(); + S32 delta_width = width - rect.getWidth(); + S32 delta_height = height - rect.getHeight(); + + if (delta_width || delta_height) + { + // adjust our rectangle + rect.mRight = getRect().mLeft + width; + rect.mTop = getRect().mBottom + height; + setRect(rect); + } + + // reshapeToFitText reshapes label to minimal size according to last bounding box + // it will work fine in case of decrease of space, but if we get more space or text + // becomes longer, label will fail to grow so reinit label's dimentions. + + LLRect label_rect = mLabel->getRect(); + S32 new_width = rect.getWidth() - label_rect.mLeft; + mLabel->reshape(new_width, label_rect.getHeight(), true); + + S32 label_top = label_rect.mTop; + mLabel->reshapeToFitText(true); + + label_rect = mLabel->getRect(); + if (label_top != label_rect.mTop && mWordWrap == WRAP_DOWN) + { + // reshapeToFitText uses LLView::reshape() which always reshapes + // from bottom to top, but we want to extend the bottom so + // reposition control + S32 delta = label_top - label_rect.mTop; + label_rect.translate(0, delta); + mLabel->setRect(label_rect); + } + + // Button + // Note: button cover the label by extending all the way to the right and down. + LLRect btn_rect = mButton->getRect(); + btn_rect.setOriginAndSize( + btn_rect.mLeft, + llmin(btn_rect.mBottom, label_rect.mBottom), + llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft), + llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight())); + mButton->setShape(btn_rect); + + updateBoundingRect(); +} + +//virtual +void LLCheckBoxCtrl::setValue(const LLSD& value ) +{ + mButton->setValue( value ); +} + +//virtual +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 ); + reshape(getRect().getWidth(), getRect().getHeight(), false); +} + +std::string LLCheckBoxCtrl::getLabel() const +{ + return mLabel->getText(); +} + +bool LLCheckBoxCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + bool res = mLabel->setTextArg(key, text); + reshape(getRect().getWidth(), getRect().getHeight(), false); + return res; +} + +// virtual +void LLCheckBoxCtrl::setControlName(const std::string& control_name, LLView* context) +{ + mButton->setControlName(control_name, context); +} + + +// virtual Returns true if the user has modified this control. +bool LLCheckBoxCtrl::isDirty() const +{ + if ( mButton ) + { + return mButton->isDirty(); + } + return false; // Shouldn't get here +} + + +// virtual Clear dirty state +void LLCheckBoxCtrl::resetDirty() +{ + if ( mButton ) + { + mButton->resetDirty(); + } +} diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h index 0312e57511..3058e946c3 100644 --- a/indra/llui/llcheckboxctrl.h +++ b/indra/llui/llcheckboxctrl.h @@ -1,157 +1,157 @@ -/** - * @file llcheckboxctrl.h - * @brief LLCheckBoxCtrl base class - * - * $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$ - */ - -#ifndef LL_LLCHECKBOXCTRL_H -#define LL_LLCHECKBOXCTRL_H - -#include "lluictrl.h" -#include "llbutton.h" -#include "lltextbox.h" -#include "v4color.h" - -// -// Constants -// - -const bool RADIO_STYLE = true; -const bool CHECK_STYLE = false; - -// -// Classes -// -class LLFontGL; -class LLViewBorder; - -class LLCheckBoxCtrl -: public LLUICtrl -, public ll::ui::SearchableControl -{ -public: - - enum EWordWrap - { - WRAP_NONE, - WRAP_UP, - WRAP_DOWN - }; - - struct WordWrap : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - struct Params - : public LLInitParam::Block - { - Optional initial_value; // override LLUICtrl initial_value - - Optional label_text; - Optional check_button; - - Optional word_wrap; - - Ignored radio_style; - - Params(); - }; - - virtual ~LLCheckBoxCtrl(); - -protected: - LLCheckBoxCtrl(const Params&); - friend class LLUICtrlFactory; - -public: - // LLView interface - - virtual void setEnabled( bool b ); - - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - - // LLUICtrl interface - virtual void setValue(const LLSD& value ); - virtual LLSD getValue() const; - bool get() { return (bool)getValue().asBoolean(); } - void set(bool value) { setValue(value); } - - virtual void setTentative(bool b); - virtual bool getTentative() const; - - virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - - virtual void clear(); - virtual void onCommit(); - - // LLCheckBoxCtrl interface - virtual bool toggle() { return mButton->toggleState(); } // returns new state - - void setBtnFocus() { mButton->setFocus(true); } - - void setEnabledColor( const LLColor4 &color ) { mTextEnabledColor = color; } - void setDisabledColor( const LLColor4 &color ) { mTextDisabledColor = color; } - - void setLabel( const LLStringExplicit& label ); - std::string getLabel() const; - - void setFont( const LLFontGL* font ) { mFont = font; } - const LLFontGL* getFont() const { return mFont; } - - virtual void setControlName(const std::string& control_name, LLView* context); - - virtual bool isDirty() const; // Returns true if the user has modified this control. - virtual void resetDirty(); // Clear dirty state - -protected: - virtual std::string _getSearchText() const - { - return getLabel() + getToolTip(); - } - - virtual void onSetHighlight() const // When highlight, really do highlight the label - { - if( mLabel ) - mLabel->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() ); - } - -protected: - // note: value is stored in toggle state of button - LLButton* mButton; - LLTextBox* mLabel; - const LLFontGL* mFont; - - LLUIColor mTextEnabledColor; - LLUIColor mTextDisabledColor; - - EWordWrap mWordWrap; // off, shifts text up, shifts text down -}; - -// Build time optimization, generate once in .cpp file -#ifndef LLCHECKBOXCTRL_CPP -extern template class LLCheckBoxCtrl* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -#endif // LL_LLCHECKBOXCTRL_H +/** + * @file llcheckboxctrl.h + * @brief LLCheckBoxCtrl base class + * + * $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$ + */ + +#ifndef LL_LLCHECKBOXCTRL_H +#define LL_LLCHECKBOXCTRL_H + +#include "lluictrl.h" +#include "llbutton.h" +#include "lltextbox.h" +#include "v4color.h" + +// +// Constants +// + +const bool RADIO_STYLE = true; +const bool CHECK_STYLE = false; + +// +// Classes +// +class LLFontGL; +class LLViewBorder; + +class LLCheckBoxCtrl +: public LLUICtrl +, public ll::ui::SearchableControl +{ +public: + + enum EWordWrap + { + WRAP_NONE, + WRAP_UP, + WRAP_DOWN + }; + + struct WordWrap : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + struct Params + : public LLInitParam::Block + { + Optional initial_value; // override LLUICtrl initial_value + + Optional label_text; + Optional check_button; + + Optional word_wrap; + + Ignored radio_style; + + Params(); + }; + + virtual ~LLCheckBoxCtrl(); + +protected: + LLCheckBoxCtrl(const Params&); + friend class LLUICtrlFactory; + +public: + // LLView interface + + virtual void setEnabled( bool b ); + + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + + // LLUICtrl interface + virtual void setValue(const LLSD& value ); + virtual LLSD getValue() const; + bool get() { return (bool)getValue().asBoolean(); } + void set(bool value) { setValue(value); } + + virtual void setTentative(bool b); + virtual bool getTentative() const; + + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); + + virtual void clear(); + virtual void onCommit(); + + // LLCheckBoxCtrl interface + virtual bool toggle() { return mButton->toggleState(); } // returns new state + + void setBtnFocus() { mButton->setFocus(true); } + + void setEnabledColor( const LLColor4 &color ) { mTextEnabledColor = color; } + void setDisabledColor( const LLColor4 &color ) { mTextDisabledColor = color; } + + void setLabel( const LLStringExplicit& label ); + std::string getLabel() const; + + void setFont( const LLFontGL* font ) { mFont = font; } + const LLFontGL* getFont() const { return mFont; } + + virtual void setControlName(const std::string& control_name, LLView* context); + + virtual bool isDirty() const; // Returns true if the user has modified this control. + virtual void resetDirty(); // Clear dirty state + +protected: + virtual std::string _getSearchText() const + { + return getLabel() + getToolTip(); + } + + virtual void onSetHighlight() const // When highlight, really do highlight the label + { + if( mLabel ) + mLabel->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() ); + } + +protected: + // note: value is stored in toggle state of button + LLButton* mButton; + LLTextBox* mLabel; + const LLFontGL* mFont; + + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; + + EWordWrap mWordWrap; // off, shifts text up, shifts text down +}; + +// Build time optimization, generate once in .cpp file +#ifndef LLCHECKBOXCTRL_CPP +extern template class LLCheckBoxCtrl* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +#endif // LL_LLCHECKBOXCTRL_H diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 305263e0cc..0d82f29dfb 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -1,1231 +1,1231 @@ -/** - * @file llcombobox.cpp - * @brief LLComboBox base class - * - * $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$ - */ - -// A control that displays the name of the chosen item, which when -// clicked shows a scrolling box of options. - -#include "linden_common.h" - -// file includes -#include "llcombobox.h" - -// common includes -#include "llstring.h" - -// newview includes -#include "llbutton.h" -#include "llkeyboard.h" -#include "llscrolllistctrl.h" -#include "llwindow.h" -#include "llfloater.h" -#include "llscrollbar.h" -#include "llscrolllistcell.h" -#include "llscrolllistitem.h" -#include "llcontrol.h" -#include "llfocusmgr.h" -#include "lllineeditor.h" -#include "v2math.h" -#include "lluictrlfactory.h" -#include "lltooltip.h" - -// Globals -S32 MAX_COMBO_WIDTH = 500; - -static LLDefaultChildRegistry::Register register_combo_box("combo_box"); - -void LLComboBox::PreferredPositionValues::declareValues() -{ - declare("above", ABOVE); - declare("below", BELOW); -} - -LLComboBox::ItemParams::ItemParams() -: label("label") -{ -} - - -LLComboBox::Params::Params() -: allow_text_entry("allow_text_entry", false), - allow_new_values("allow_new_values", false), - show_text_as_tentative("show_text_as_tentative", true), - max_chars("max_chars", 20), - list_position("list_position", BELOW), - items("item"), - combo_button("combo_button"), - combo_list("combo_list"), - combo_editor("combo_editor"), - drop_down_button("drop_down_button") -{ - addSynonym(items, "combo_item"); -} - - -LLComboBox::LLComboBox(const LLComboBox::Params& p) -: LLUICtrl(p), - mTextEntry(NULL), - mTextEntryTentative(p.show_text_as_tentative), - mHasAutocompletedText(false), - mAllowTextEntry(p.allow_text_entry), - mAllowNewValues(p.allow_new_values), - mMaxChars(p.max_chars), - mPrearrangeCallback(p.prearrange_callback()), - mTextEntryCallback(p.text_entry_callback()), - mTextChangedCallback(p.text_changed_callback()), - mListPosition(p.list_position), - mLastSelectedIndex(-1), - mLabel(p.label) -{ - // Text label button - - LLButton::Params button_params = (mAllowTextEntry ? p.combo_button : p.drop_down_button); - button_params.mouse_down_callback.function( - boost::bind(&LLComboBox::onButtonMouseDown, this)); - button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT); - button_params.rect(p.rect); - - if(mAllowTextEntry) - { - button_params.pad_right(2); - } - - mArrowImage = button_params.image_unselected; - if (mArrowImage.notNull()) - { - mImageLoadedConnection = mArrowImage->addLoadedCallback(boost::bind(&LLComboBox::imageLoaded, this)); - } - - mButton = LLUICtrlFactory::create(button_params); - - - if(mAllowTextEntry) - { - //redo to compensate for button hack that leaves space for a character - //unless it is a "minimal combobox"(drop down) - mButton->setRightHPad(2); - } - addChild(mButton); - - LLScrollListCtrl::Params params = p.combo_list; - params.name("ComboBox"); - params.commit_callback.function(boost::bind(&LLComboBox::onItemSelected, this, _2)); - params.visible(false); - params.commit_on_keyboard_movement(false); - - mList = LLUICtrlFactory::create(params); - addChild(mList); - - // Mouse-down on button will transfer mouse focus to the list - // Grab the mouse-up event and make sure the button state is correct - mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this)); - - for (LLInitParam::ParamIterator::const_iterator it = p.items.begin(); - it != p.items.end(); - ++it) - { - LLScrollListItem::Params item_params = *it; - if (it->label.isProvided()) - { - item_params.columns.add().value(it->label()); - } - - mList->addRow(item_params); - } - - createLineEditor(p); - - mTopLostSignalConnection = setTopLostCallback(boost::bind(&LLComboBox::hideList, this)); -} - -void LLComboBox::initFromParams(const LLComboBox::Params& p) -{ - LLUICtrl::initFromParams(p); - - if (!acceptsTextInput() && mLabel.empty()) - { - selectFirstItem(); - } -} - -// virtual -bool LLComboBox::postBuild() -{ - if (mControlVariable) - { - setValue(mControlVariable->getValue()); // selects the appropriate item - } - return true; -} - - -LLComboBox::~LLComboBox() -{ - // children automatically deleted, including mMenu, mButton - - // explicitly disconect this signal, since base class destructor might fire top lost - mTopLostSignalConnection.disconnect(); - mImageLoadedConnection.disconnect(); - - LLUI::getInstance()->removePopup(this); -} - - -void LLComboBox::clear() -{ - if (mTextEntry) - { - mTextEntry->setText(LLStringUtil::null); - } - mButton->setLabelSelected(LLStringUtil::null); - mButton->setLabelUnselected(LLStringUtil::null); - mList->deselectAllItems(); - mLastSelectedIndex = -1; -} - -void LLComboBox::onCommit() -{ - if (mAllowTextEntry && getCurrentIndex() != -1) - { - // we have selected an existing item, blitz the manual text entry with - // the properly capitalized item - mTextEntry->setValue(getSimple()); - mTextEntry->setTentative(false); - } - setControlValue(getValue()); - LLUICtrl::onCommit(); -} - -// virtual -bool LLComboBox::isDirty() const -{ - bool grubby = false; - if ( mList ) - { - grubby = mList->isDirty(); - } - return grubby; -} - -// virtual Clear dirty state -void LLComboBox::resetDirty() -{ - if ( mList ) - { - mList->resetDirty(); - } -} - -bool LLComboBox::itemExists(const std::string& name) -{ - return mList->getItemByLabel(name); -} - -// add item "name" to menu -LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, bool enabled) -{ - LLScrollListItem* item = mList->addSimpleElement(name, pos); - item->setEnabled(enabled); - if (!mAllowTextEntry && mLabel.empty()) - { - if (mControlVariable) - { - setValue(mControlVariable->getValue()); // selects the appropriate item - } - else - { - selectFirstItem(); - } - } - return item; -} - -// add item "name" with a unique id to menu -LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, bool enabled ) -{ - LLScrollListItem* item = mList->addSimpleElement(name, pos, id); - item->setEnabled(enabled); - if (!mAllowTextEntry && mLabel.empty()) - { - if (mControlVariable) - { - setValue(mControlVariable->getValue()); // selects the appropriate item - } - else - { - selectFirstItem(); - } - } - return item; -} - -// add item "name" with attached userdata -LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, bool enabled ) -{ - LLScrollListItem* item = mList->addSimpleElement(name, pos); - item->setEnabled(enabled); - item->setUserdata( userdata ); - if (!mAllowTextEntry && mLabel.empty()) - { - if (mControlVariable) - { - setValue(mControlVariable->getValue()); // selects the appropriate item - } - else - { - selectFirstItem(); - } - } - return item; -} - -// add item "name" with attached generic data -LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, bool enabled ) -{ - LLScrollListItem* item = mList->addSimpleElement(name, pos, value); - item->setEnabled(enabled); - if (!mAllowTextEntry && mLabel.empty()) - { - if (mControlVariable) - { - setValue(mControlVariable->getValue()); // selects the appropriate item - } - else - { - selectFirstItem(); - } - } - return item; -} - -LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos) -{ - return mList->addSeparator(pos); -} - -void LLComboBox::sortByName(bool ascending) -{ - mList->sortOnce(0, ascending); -} - - -// Choose an item with a given name in the menu. -// Returns true if the item was found. -bool LLComboBox::setSimple(const LLStringExplicit& name) -{ - bool found = mList->selectItemByLabel(name, false); - - if (found) - { - setLabel(name); - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - - return found; -} - -// virtual -void LLComboBox::setValue(const LLSD& value) -{ - bool found = mList->selectByValue(value); - if (found) - { - LLScrollListItem* item = mList->getFirstSelected(); - if (item) - { - updateLabel(); - } - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - else - { - mLastSelectedIndex = -1; - } -} - -const std::string LLComboBox::getSimple() const -{ - const std::string res = getSelectedItemLabel(); - if (res.empty() && mAllowTextEntry) - { - return mTextEntry->getText(); - } - else - { - return res; - } -} - -const std::string LLComboBox::getSelectedItemLabel(S32 column) const -{ - return mList->getSelectedItemLabel(column); -} - -// virtual -LLSD LLComboBox::getValue() const -{ - LLScrollListItem* item = mList->getFirstSelected(); - if( item ) - { - return item->getValue(); - } - else if (mAllowTextEntry) - { - return mTextEntry->getValue(); - } - else - { - return LLSD(); - } -} - -void LLComboBox::setLabel(const LLStringExplicit& name) -{ - if ( mTextEntry ) - { - mTextEntry->setText(name); - if (mList->selectItemByLabel(name, false)) - { - mTextEntry->setTentative(false); - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - else - { - mTextEntry->setTentative(mTextEntryTentative); - } - } - - if (!mAllowTextEntry) - { - mButton->setLabel(name); - } -} - -void LLComboBox::updateLabel() -{ - // Update the combo editor with the selected - // item label. - if (mTextEntry) - { - mTextEntry->setText(getSelectedItemLabel()); - mTextEntry->setTentative(false); - } - - // If combo box doesn't allow text entry update - // the combo button label. - if (!mAllowTextEntry) - { - mButton->setLabel(getSelectedItemLabel()); - } -} - -bool LLComboBox::remove(const std::string& name) -{ - bool found = mList->selectItemByLabel(name); - - if (found) - { - LLScrollListItem* item = mList->getFirstSelected(); - if (item) - { - mList->deleteSingleItem(mList->getItemIndex(item)); - } - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - - return found; -} - -bool LLComboBox::remove(S32 index) -{ - if (index < mList->getItemCount()) - { - mList->deleteSingleItem(index); - setLabel(getSelectedItemLabel()); - return true; - } - return false; -} - -// Keyboard focus lost. -void LLComboBox::onFocusLost() -{ - hideList(); - // if valid selection - if (mAllowTextEntry && getCurrentIndex() != -1) - { - mTextEntry->selectAll(); - } - mButton->setForcePressedState(false); - LLUICtrl::onFocusLost(); -} - -void LLComboBox::setButtonVisible(bool visible) -{ - mButton->setVisible(visible); - if (mTextEntry) - { - LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); - if (visible) - { - S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; - text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW; - } - //mTextEntry->setRect(text_entry_rect); - mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true); - } -} - -bool LLComboBox::setCurrentByIndex( S32 index ) -{ - bool found = mList->selectNthItem( index ); - if (found) - { - setLabel(getSelectedItemLabel()); - mLastSelectedIndex = index; - } - return found; -} - -S32 LLComboBox::getCurrentIndex() const -{ - LLScrollListItem* item = mList->getFirstSelected(); - if( item ) - { - return mList->getItemIndex( item ); - } - return -1; -} - -void LLComboBox::setEnabledByValue(const LLSD& value, bool enabled) -{ - LLScrollListItem *found = mList->getItem(value); - if (found) - { - found->setEnabled(enabled); - } -} - -void LLComboBox::createLineEditor(const LLComboBox::Params& p) -{ - LLRect rect = getLocalRect(); - if (mAllowTextEntry) - { - S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; - S32 shadow_size = BTN_DROP_SHADOW; - mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size, - rect.mTop, rect.mRight, rect.mBottom)); - mButton->setTabStop(false); - mButton->setHAlign(LLFontGL::HCENTER); - - LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); - text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW; - // clear label on button - std::string cur_label = mButton->getLabelSelected(); - LLLineEditor::Params params = p.combo_editor; - params.rect(text_entry_rect); - params.default_text(LLStringUtil::null); - params.max_length.bytes(mMaxChars); - params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2)); - params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1)); - params.commit_on_focus_lost(false); - params.follows.flags(FOLLOWS_ALL); - params.label(mLabel); - mTextEntry = LLUICtrlFactory::create (params); - mTextEntry->setText(cur_label); - mTextEntry->setIgnoreTab(true); - addChild(mTextEntry); - - // clear label on button - setLabel(LLStringUtil::null); - - mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); - } - else - { - mButton->setRect(rect); - mButton->setLabel(mLabel.getString()); - - if (mTextEntry) - { - mTextEntry->setVisible(false); - } - } -} - -void LLComboBox::setLeftTextPadding(S32 pad) -{ - S32 left_pad, right_pad; - mTextEntry->getTextPadding(&left_pad, &right_pad); - mTextEntry->setTextPadding(pad, right_pad); -} - -void* LLComboBox::getCurrentUserdata() -{ - LLScrollListItem* item = mList->getFirstSelected(); - if( item ) - { - return item->getUserdata(); - } - return NULL; -} - - -void LLComboBox::showList() -{ - // Make sure we don't go off top of screen. - LLCoordWindow window_size; - getWindow()->getSize(&window_size); - //HACK: shouldn't have to know about scale here - mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::getScaleFactor().mV[VY]) - 50 ); - - // Make sure that we can see the whole list - LLRect root_view_local; - LLView* root_view = getRootView(); - root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this); - - LLRect rect = mList->getRect(); - - S32 min_width = getRect().getWidth(); - S32 max_width = llmax(min_width, MAX_COMBO_WIDTH); - // make sure we have up to date content width metrics - S32 list_width = llclamp(mList->calcMaxContentWidth(), min_width, max_width); - - if (mListPosition == BELOW) - { - if (rect.getHeight() <= -root_view_local.mBottom) - { - // Move rect so it hangs off the bottom of this view - rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() ); - } - else - { - // stack on top or bottom, depending on which has more room - if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight()) - { - // Move rect so it hangs off the bottom of this view - rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight())); - } - else - { - // move rect so it stacks on top of this view (clipped to size of screen) - rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); - } - } - } - else // ABOVE - { - if (rect.getHeight() <= root_view_local.mTop - getRect().getHeight()) - { - // move rect so it stacks on top of this view (clipped to size of screen) - rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); - } - else - { - // stack on top or bottom, depending on which has more room - if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight()) - { - // Move rect so it hangs off the bottom of this view - rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight())); - } - else - { - // move rect so it stacks on top of this view (clipped to size of screen) - rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); - } - } - - } - mList->setOrigin(rect.mLeft, rect.mBottom); - mList->reshape(rect.getWidth(), rect.getHeight()); - mList->translateIntoRect(root_view_local); - - // Make sure we didn't go off bottom of screen - S32 x, y; - mList->localPointToScreen(0, 0, &x, &y); - - if (y < 0) - { - mList->translate(0, -y); - } - - // NB: this call will trigger the focuslost callback which will hide the list, so do it first - // before finally showing the list - - mList->setFocus(true); - - // Show the list and push the button down - mButton->setToggleState(true); - mList->setVisible(true); - - LLUI::getInstance()->addPopup(this); - - setUseBoundingRect(true); -// updateBoundingRect(); -} - -void LLComboBox::hideList() -{ - if (mList->getVisible()) - { - // assert selection in list - if(mAllowNewValues) - { - // mLastSelectedIndex = -1 means that we entered a new value, don't select - // any of existing items in this case. - if(mLastSelectedIndex >= 0) - mList->selectNthItem(mLastSelectedIndex); - } - else if(mLastSelectedIndex >= 0) - mList->selectNthItem(mLastSelectedIndex); - - mButton->setToggleState(false); - mList->setVisible(false); - mList->mouseOverHighlightNthItem(-1); - - setUseBoundingRect(false); - LLUI::getInstance()->removePopup(this); -// updateBoundingRect(); - } -} - -void LLComboBox::onButtonMouseDown() -{ - if (!mList->getVisible()) - { - // this might change selection, so do it first - prearrangeList(); - - // highlight the last selected item from the original selection before potentially selecting a new item - // as visual cue to original value of combo box - LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); - if (last_selected_item) - { - mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); - } - - if (mList->getItemCount() != 0) - { - showList(); - } - - setFocus( true ); - - // pass mouse capture on to list if button is depressed - if (mButton->hasMouseCapture()) - { - gFocusMgr.setMouseCapture(mList); - - // But keep the "pressed" look, which buttons normally lose when they - // lose focus - mButton->setForcePressedState(true); - } - } - else - { - hideList(); - } - -} - -void LLComboBox::onListMouseUp() -{ - // In some cases this is the termination of a mouse click that started on - // the button, so clear its pressed state - mButton->setForcePressedState(false); -} - -//------------------------------------------------------------------ -// static functions -//------------------------------------------------------------------ - -void LLComboBox::onItemSelected(const LLSD& data) -{ - mLastSelectedIndex = getCurrentIndex(); - if (mLastSelectedIndex != -1) - { - updateLabel(); - - if (mAllowTextEntry) - { - gFocusMgr.setKeyboardFocus(mTextEntry); - mTextEntry->selectAll(); - } - } - // hiding the list reasserts the old value stored in the text editor/dropdown button - hideList(); - - // commit does the reverse, asserting the value in the list - onCommit(); -} - -bool LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) -{ - std::string tool_tip; - - if(LLUICtrl::handleToolTip(x, y, mask)) - { - return true; - } - - tool_tip = getToolTip(); - if (tool_tip.empty()) - { - tool_tip = getSelectedItemLabel(); - } - - if( !tool_tip.empty() ) - { - LLToolTipMgr::instance().show(LLToolTip::Params() - .message(tool_tip) - .sticky_rect(calcScreenRect())); - } - return true; -} - -bool LLComboBox::handleKeyHere(KEY key, MASK mask) -{ - bool result = false; - if (hasFocus()) - { - if (mList->getVisible() - && key == KEY_ESCAPE && mask == MASK_NONE) - { - hideList(); - return true; - } - //give list a chance to pop up and handle key - LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); - if (last_selected_item) - { - // highlight the original selection before potentially selecting a new item - mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); - } - result = mList->handleKeyHere(key, mask); - - // will only see return key if it is originating from line editor - // since the dropdown button eats the key - if (key == KEY_RETURN) - { - if (mask == MASK_NONE) - { - mOnReturnSignal(this, getValue()); - } - - // don't show list and don't eat key input when committing - // free-form text entry with RETURN since user already knows - // what they are trying to select - return false; - } - // if selection has changed, pop open list - else if (mList->getLastSelectedItem() != last_selected_item - || ((key == KEY_DOWN || key == KEY_UP) - && mList->getCanSelect() - && !mList->isEmpty())) - { - showList(); - } - } - return result; -} - -bool LLComboBox::handleUnicodeCharHere(llwchar uni_char) -{ - bool result = false; - if (gFocusMgr.childHasKeyboardFocus(this)) - { - // space bar just shows the list - if (' ' != uni_char ) - { - LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); - if (last_selected_item) - { - // highlight the original selection before potentially selecting a new item - mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); - } - result = mList->handleUnicodeCharHere(uni_char); - if (mList->getLastSelectedItem() != last_selected_item) - { - showList(); - } - } - } - return result; -} - -void LLComboBox::setTextEntry(const LLStringExplicit& text) -{ - if (mTextEntry) - { - mTextEntry->setText(text); - mHasAutocompletedText = false; - updateSelection(); - } -} - -void LLComboBox::setKeystrokeOnEsc(bool enable) -{ - if (mTextEntry) - { - mTextEntry->setKeystrokeOnEsc(enable); - } -} - -void LLComboBox::onTextEntry(LLLineEditor* line_editor) -{ - if (mTextEntryCallback != NULL) - { - (mTextEntryCallback)(line_editor, LLSD()); - } - - KEY key = gKeyboard->currentKey(); - if (key == KEY_BACKSPACE || - key == KEY_DELETE) - { - if (mList->selectItemByLabel(line_editor->getText(), false)) - { - line_editor->setTentative(false); - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - else - { - line_editor->setTentative(mTextEntryTentative); - mList->deselectAllItems(); - mLastSelectedIndex = -1; - } - if (mTextChangedCallback != NULL) - { - (mTextChangedCallback)(line_editor, LLSD()); - } - return; - } - - if (key == KEY_LEFT || - key == KEY_RIGHT) - { - return; - } - - if (key == KEY_DOWN) - { - setCurrentByIndex(llmin(getItemCount() - 1, getCurrentIndex() + 1)); - if (!mList->getVisible()) - { - prearrangeList(); - - if (mList->getItemCount() != 0) - { - showList(); - } - } - line_editor->selectAll(); - line_editor->setTentative(false); - } - else if (key == KEY_UP) - { - setCurrentByIndex(llmax(0, getCurrentIndex() - 1)); - if (!mList->getVisible()) - { - prearrangeList(); - - if (mList->getItemCount() != 0) - { - showList(); - } - } - line_editor->selectAll(); - line_editor->setTentative(false); - } - else - { - // RN: presumably text entry - updateSelection(); - } - if (mTextChangedCallback != NULL) - { - (mTextChangedCallback)(line_editor, LLSD()); - } -} - -void LLComboBox::updateSelection() -{ - LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor()); - // user-entered portion of string, based on assumption that any selected - // text was a result of auto-completion - LLWString user_wstring = mHasAutocompletedText ? left_wstring : mTextEntry->getWText(); - std::string full_string = mTextEntry->getText(); - - // go ahead and arrange drop down list on first typed character, even - // though we aren't showing it... some code relies on prearrange - // callback to populate content - if( mTextEntry->getWText().size() == 1 ) - { - prearrangeList(mTextEntry->getText()); - } - - if (mList->selectItemByLabel(full_string, false)) - { - mTextEntry->setTentative(false); - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - else if (mList->selectItemByPrefix(left_wstring, false)) - { - LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel()); - LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size()); - mTextEntry->setText(wstring_to_utf8str(wtext)); - mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size()); - mTextEntry->endSelection(); - mTextEntry->setTentative(false); - mHasAutocompletedText = true; - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - else // no matching items found - { - mList->deselectAllItems(); - mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion - mTextEntry->setTentative(mTextEntryTentative); - mHasAutocompletedText = false; - mLastSelectedIndex = -1; - } -} - -void LLComboBox::onTextCommit(const LLSD& data) -{ - std::string text = mTextEntry->getText(); - setSimple(text); - onCommit(); - mTextEntry->selectAll(); -} - -void LLComboBox::setFocus(bool b) -{ - LLUICtrl::setFocus(b); - - if (b) - { - mList->clearSearchString(); - if (mList->getVisible()) - { - mList->setFocus(true); - } - } -} - -void LLComboBox::prearrangeList(std::string filter) -{ - if (mPrearrangeCallback) - { - mPrearrangeCallback(this, LLSD(filter)); - } -} - - -//============================================================================ -// ll::ui::SearchableControl functions - -//virtual -std::string LLComboBox::_getSearchText() const -{ - std::string res; - if (mList) - { - // getAllData returns a full copy of content, might be a - // better option to implement an mList->getSearchText(column) - std::vector data = mList->getAllData(); - std::vector::iterator iter = data.begin(); - while (iter != data.end()) - { - LLScrollListCell* cell = (*iter)->getColumn(0); - if (cell) - { - std::string whitelist_url = cell->getValue().asString(); - res += cell->getValue().asString(); - } - iter++; - } - } - return res + getToolTip(); -} - -//virtual -void LLComboBox::onSetHighlight() const -{ - if (mButton) - { - mButton->ll::ui::SearchableControl::setHighlighted(ll::ui::SearchableControl::getHighlighted()); - } -} - -void LLComboBox::imageLoaded() -{ - if (mAllowTextEntry) - { - LLRect rect = getLocalRect(); - S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; - S32 shadow_size = BTN_DROP_SHADOW; - mButton->setRect(LLRect(getRect().getWidth() - llmax(8, arrow_width) - 2 * shadow_size, - rect.mTop, rect.mRight, rect.mBottom)); - if (mButton->getVisible()) - { - // recalculate field size - if (mTextEntry) - { - LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); - text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * BTN_DROP_SHADOW; - mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true); - } - } - } -} - -//============================================================================ -// LLCtrlListInterface functions - -S32 LLComboBox::getItemCount() const -{ - return mList->getItemCount(); -} - -void LLComboBox::addColumn(const LLSD& column, EAddPosition pos) -{ - mList->clearColumns(); - mList->addColumn(column, pos); -} - -void LLComboBox::clearColumns() -{ - mList->clearColumns(); -} - -void LLComboBox::setColumnLabel(const std::string& column, const std::string& label) -{ - mList->setColumnLabel(column, label); -} - -LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata) -{ - return mList->addElement(value, pos, userdata); -} - -LLScrollListItem* LLComboBox::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) -{ - return mList->addSimpleElement(value, pos, id); -} - -void LLComboBox::clearRows() -{ - mList->clearRows(); -} - -void LLComboBox::sortByColumn(const std::string& name, bool ascending) -{ - mList->sortByColumn(name, ascending); -} - -//============================================================================ -//LLCtrlSelectionInterface functions - -bool LLComboBox::setCurrentByID(const LLUUID& id) -{ - bool found = mList->selectByID( id ); - - if (found) - { - setLabel(getSelectedItemLabel()); - mLastSelectedIndex = mList->getFirstSelectedIndex(); - } - - return found; -} - -LLUUID LLComboBox::getCurrentID() const -{ - return mList->getStringUUIDSelectedItem(); -} -bool LLComboBox::setSelectedByValue(const LLSD& value, bool selected) -{ - bool found = mList->setSelectedByValue(value, selected); - if (found) - { - setLabel(getSelectedItemLabel()); - } - return found; -} - -LLSD LLComboBox::getSelectedValue() -{ - return mList->getSelectedValue(); -} - -bool LLComboBox::isSelected(const LLSD& value) const -{ - return mList->isSelected(value); -} - -bool LLComboBox::operateOnSelection(EOperation op) -{ - if (op == OP_DELETE) - { - mList->deleteSelectedItems(); - return true; - } - return false; -} - -bool LLComboBox::operateOnAll(EOperation op) -{ - if (op == OP_DELETE) - { - clearRows(); - return true; - } - return false; -} - -bool LLComboBox::selectItemRange( S32 first, S32 last ) -{ - return mList->selectItemRange(first, last); -} - - -static LLDefaultChildRegistry::Register register_icons_combo_box("icons_combo_box"); - -LLIconsComboBox::Params::Params() -: icon_column("icon_column", ICON_COLUMN), - label_column("label_column", LABEL_COLUMN) -{} - -LLIconsComboBox::LLIconsComboBox(const LLIconsComboBox::Params& p) -: LLComboBox(p), - mIconColumnIndex(p.icon_column), - mLabelColumnIndex(p.label_column) -{} - -const std::string LLIconsComboBox::getSelectedItemLabel(S32 column) const -{ - mButton->setImageOverlay(LLComboBox::getSelectedItemLabel(mIconColumnIndex), mButton->getImageOverlayHAlign()); - - return LLComboBox::getSelectedItemLabel(mLabelColumnIndex); -} +/** + * @file llcombobox.cpp + * @brief LLComboBox base class + * + * $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$ + */ + +// A control that displays the name of the chosen item, which when +// clicked shows a scrolling box of options. + +#include "linden_common.h" + +// file includes +#include "llcombobox.h" + +// common includes +#include "llstring.h" + +// newview includes +#include "llbutton.h" +#include "llkeyboard.h" +#include "llscrolllistctrl.h" +#include "llwindow.h" +#include "llfloater.h" +#include "llscrollbar.h" +#include "llscrolllistcell.h" +#include "llscrolllistitem.h" +#include "llcontrol.h" +#include "llfocusmgr.h" +#include "lllineeditor.h" +#include "v2math.h" +#include "lluictrlfactory.h" +#include "lltooltip.h" + +// Globals +S32 MAX_COMBO_WIDTH = 500; + +static LLDefaultChildRegistry::Register register_combo_box("combo_box"); + +void LLComboBox::PreferredPositionValues::declareValues() +{ + declare("above", ABOVE); + declare("below", BELOW); +} + +LLComboBox::ItemParams::ItemParams() +: label("label") +{ +} + + +LLComboBox::Params::Params() +: allow_text_entry("allow_text_entry", false), + allow_new_values("allow_new_values", false), + show_text_as_tentative("show_text_as_tentative", true), + max_chars("max_chars", 20), + list_position("list_position", BELOW), + items("item"), + combo_button("combo_button"), + combo_list("combo_list"), + combo_editor("combo_editor"), + drop_down_button("drop_down_button") +{ + addSynonym(items, "combo_item"); +} + + +LLComboBox::LLComboBox(const LLComboBox::Params& p) +: LLUICtrl(p), + mTextEntry(NULL), + mTextEntryTentative(p.show_text_as_tentative), + mHasAutocompletedText(false), + mAllowTextEntry(p.allow_text_entry), + mAllowNewValues(p.allow_new_values), + mMaxChars(p.max_chars), + mPrearrangeCallback(p.prearrange_callback()), + mTextEntryCallback(p.text_entry_callback()), + mTextChangedCallback(p.text_changed_callback()), + mListPosition(p.list_position), + mLastSelectedIndex(-1), + mLabel(p.label) +{ + // Text label button + + LLButton::Params button_params = (mAllowTextEntry ? p.combo_button : p.drop_down_button); + button_params.mouse_down_callback.function( + boost::bind(&LLComboBox::onButtonMouseDown, this)); + button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT); + button_params.rect(p.rect); + + if(mAllowTextEntry) + { + button_params.pad_right(2); + } + + mArrowImage = button_params.image_unselected; + if (mArrowImage.notNull()) + { + mImageLoadedConnection = mArrowImage->addLoadedCallback(boost::bind(&LLComboBox::imageLoaded, this)); + } + + mButton = LLUICtrlFactory::create(button_params); + + + if(mAllowTextEntry) + { + //redo to compensate for button hack that leaves space for a character + //unless it is a "minimal combobox"(drop down) + mButton->setRightHPad(2); + } + addChild(mButton); + + LLScrollListCtrl::Params params = p.combo_list; + params.name("ComboBox"); + params.commit_callback.function(boost::bind(&LLComboBox::onItemSelected, this, _2)); + params.visible(false); + params.commit_on_keyboard_movement(false); + + mList = LLUICtrlFactory::create(params); + addChild(mList); + + // Mouse-down on button will transfer mouse focus to the list + // Grab the mouse-up event and make sure the button state is correct + mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this)); + + for (LLInitParam::ParamIterator::const_iterator it = p.items.begin(); + it != p.items.end(); + ++it) + { + LLScrollListItem::Params item_params = *it; + if (it->label.isProvided()) + { + item_params.columns.add().value(it->label()); + } + + mList->addRow(item_params); + } + + createLineEditor(p); + + mTopLostSignalConnection = setTopLostCallback(boost::bind(&LLComboBox::hideList, this)); +} + +void LLComboBox::initFromParams(const LLComboBox::Params& p) +{ + LLUICtrl::initFromParams(p); + + if (!acceptsTextInput() && mLabel.empty()) + { + selectFirstItem(); + } +} + +// virtual +bool LLComboBox::postBuild() +{ + if (mControlVariable) + { + setValue(mControlVariable->getValue()); // selects the appropriate item + } + return true; +} + + +LLComboBox::~LLComboBox() +{ + // children automatically deleted, including mMenu, mButton + + // explicitly disconect this signal, since base class destructor might fire top lost + mTopLostSignalConnection.disconnect(); + mImageLoadedConnection.disconnect(); + + LLUI::getInstance()->removePopup(this); +} + + +void LLComboBox::clear() +{ + if (mTextEntry) + { + mTextEntry->setText(LLStringUtil::null); + } + mButton->setLabelSelected(LLStringUtil::null); + mButton->setLabelUnselected(LLStringUtil::null); + mList->deselectAllItems(); + mLastSelectedIndex = -1; +} + +void LLComboBox::onCommit() +{ + if (mAllowTextEntry && getCurrentIndex() != -1) + { + // we have selected an existing item, blitz the manual text entry with + // the properly capitalized item + mTextEntry->setValue(getSimple()); + mTextEntry->setTentative(false); + } + setControlValue(getValue()); + LLUICtrl::onCommit(); +} + +// virtual +bool LLComboBox::isDirty() const +{ + bool grubby = false; + if ( mList ) + { + grubby = mList->isDirty(); + } + return grubby; +} + +// virtual Clear dirty state +void LLComboBox::resetDirty() +{ + if ( mList ) + { + mList->resetDirty(); + } +} + +bool LLComboBox::itemExists(const std::string& name) +{ + return mList->getItemByLabel(name); +} + +// add item "name" to menu +LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, bool enabled) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos); + item->setEnabled(enabled); + if (!mAllowTextEntry && mLabel.empty()) + { + if (mControlVariable) + { + setValue(mControlVariable->getValue()); // selects the appropriate item + } + else + { + selectFirstItem(); + } + } + return item; +} + +// add item "name" with a unique id to menu +LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, bool enabled ) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos, id); + item->setEnabled(enabled); + if (!mAllowTextEntry && mLabel.empty()) + { + if (mControlVariable) + { + setValue(mControlVariable->getValue()); // selects the appropriate item + } + else + { + selectFirstItem(); + } + } + return item; +} + +// add item "name" with attached userdata +LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, bool enabled ) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos); + item->setEnabled(enabled); + item->setUserdata( userdata ); + if (!mAllowTextEntry && mLabel.empty()) + { + if (mControlVariable) + { + setValue(mControlVariable->getValue()); // selects the appropriate item + } + else + { + selectFirstItem(); + } + } + return item; +} + +// add item "name" with attached generic data +LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, bool enabled ) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos, value); + item->setEnabled(enabled); + if (!mAllowTextEntry && mLabel.empty()) + { + if (mControlVariable) + { + setValue(mControlVariable->getValue()); // selects the appropriate item + } + else + { + selectFirstItem(); + } + } + return item; +} + +LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos) +{ + return mList->addSeparator(pos); +} + +void LLComboBox::sortByName(bool ascending) +{ + mList->sortOnce(0, ascending); +} + + +// Choose an item with a given name in the menu. +// Returns true if the item was found. +bool LLComboBox::setSimple(const LLStringExplicit& name) +{ + bool found = mList->selectItemByLabel(name, false); + + if (found) + { + setLabel(name); + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + + return found; +} + +// virtual +void LLComboBox::setValue(const LLSD& value) +{ + bool found = mList->selectByValue(value); + if (found) + { + LLScrollListItem* item = mList->getFirstSelected(); + if (item) + { + updateLabel(); + } + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else + { + mLastSelectedIndex = -1; + } +} + +const std::string LLComboBox::getSimple() const +{ + const std::string res = getSelectedItemLabel(); + if (res.empty() && mAllowTextEntry) + { + return mTextEntry->getText(); + } + else + { + return res; + } +} + +const std::string LLComboBox::getSelectedItemLabel(S32 column) const +{ + return mList->getSelectedItemLabel(column); +} + +// virtual +LLSD LLComboBox::getValue() const +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return item->getValue(); + } + else if (mAllowTextEntry) + { + return mTextEntry->getValue(); + } + else + { + return LLSD(); + } +} + +void LLComboBox::setLabel(const LLStringExplicit& name) +{ + if ( mTextEntry ) + { + mTextEntry->setText(name); + if (mList->selectItemByLabel(name, false)) + { + mTextEntry->setTentative(false); + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else + { + mTextEntry->setTentative(mTextEntryTentative); + } + } + + if (!mAllowTextEntry) + { + mButton->setLabel(name); + } +} + +void LLComboBox::updateLabel() +{ + // Update the combo editor with the selected + // item label. + if (mTextEntry) + { + mTextEntry->setText(getSelectedItemLabel()); + mTextEntry->setTentative(false); + } + + // If combo box doesn't allow text entry update + // the combo button label. + if (!mAllowTextEntry) + { + mButton->setLabel(getSelectedItemLabel()); + } +} + +bool LLComboBox::remove(const std::string& name) +{ + bool found = mList->selectItemByLabel(name); + + if (found) + { + LLScrollListItem* item = mList->getFirstSelected(); + if (item) + { + mList->deleteSingleItem(mList->getItemIndex(item)); + } + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + + return found; +} + +bool LLComboBox::remove(S32 index) +{ + if (index < mList->getItemCount()) + { + mList->deleteSingleItem(index); + setLabel(getSelectedItemLabel()); + return true; + } + return false; +} + +// Keyboard focus lost. +void LLComboBox::onFocusLost() +{ + hideList(); + // if valid selection + if (mAllowTextEntry && getCurrentIndex() != -1) + { + mTextEntry->selectAll(); + } + mButton->setForcePressedState(false); + LLUICtrl::onFocusLost(); +} + +void LLComboBox::setButtonVisible(bool visible) +{ + mButton->setVisible(visible); + if (mTextEntry) + { + LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + if (visible) + { + S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; + text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW; + } + //mTextEntry->setRect(text_entry_rect); + mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true); + } +} + +bool LLComboBox::setCurrentByIndex( S32 index ) +{ + bool found = mList->selectNthItem( index ); + if (found) + { + setLabel(getSelectedItemLabel()); + mLastSelectedIndex = index; + } + return found; +} + +S32 LLComboBox::getCurrentIndex() const +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return mList->getItemIndex( item ); + } + return -1; +} + +void LLComboBox::setEnabledByValue(const LLSD& value, bool enabled) +{ + LLScrollListItem *found = mList->getItem(value); + if (found) + { + found->setEnabled(enabled); + } +} + +void LLComboBox::createLineEditor(const LLComboBox::Params& p) +{ + LLRect rect = getLocalRect(); + if (mAllowTextEntry) + { + S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; + S32 shadow_size = BTN_DROP_SHADOW; + mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size, + rect.mTop, rect.mRight, rect.mBottom)); + mButton->setTabStop(false); + mButton->setHAlign(LLFontGL::HCENTER); + + LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW; + // clear label on button + std::string cur_label = mButton->getLabelSelected(); + LLLineEditor::Params params = p.combo_editor; + params.rect(text_entry_rect); + params.default_text(LLStringUtil::null); + params.max_length.bytes(mMaxChars); + params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2)); + params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1)); + params.commit_on_focus_lost(false); + params.follows.flags(FOLLOWS_ALL); + params.label(mLabel); + mTextEntry = LLUICtrlFactory::create (params); + mTextEntry->setText(cur_label); + mTextEntry->setIgnoreTab(true); + addChild(mTextEntry); + + // clear label on button + setLabel(LLStringUtil::null); + + mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); + } + else + { + mButton->setRect(rect); + mButton->setLabel(mLabel.getString()); + + if (mTextEntry) + { + mTextEntry->setVisible(false); + } + } +} + +void LLComboBox::setLeftTextPadding(S32 pad) +{ + S32 left_pad, right_pad; + mTextEntry->getTextPadding(&left_pad, &right_pad); + mTextEntry->setTextPadding(pad, right_pad); +} + +void* LLComboBox::getCurrentUserdata() +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return item->getUserdata(); + } + return NULL; +} + + +void LLComboBox::showList() +{ + // Make sure we don't go off top of screen. + LLCoordWindow window_size; + getWindow()->getSize(&window_size); + //HACK: shouldn't have to know about scale here + mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::getScaleFactor().mV[VY]) - 50 ); + + // Make sure that we can see the whole list + LLRect root_view_local; + LLView* root_view = getRootView(); + root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this); + + LLRect rect = mList->getRect(); + + S32 min_width = getRect().getWidth(); + S32 max_width = llmax(min_width, MAX_COMBO_WIDTH); + // make sure we have up to date content width metrics + S32 list_width = llclamp(mList->calcMaxContentWidth(), min_width, max_width); + + if (mListPosition == BELOW) + { + if (rect.getHeight() <= -root_view_local.mBottom) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() ); + } + else + { + // stack on top or bottom, depending on which has more room + if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight()) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight())); + } + else + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); + } + } + } + else // ABOVE + { + if (rect.getHeight() <= root_view_local.mTop - getRect().getHeight()) + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); + } + else + { + // stack on top or bottom, depending on which has more room + if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight()) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight())); + } + else + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); + } + } + + } + mList->setOrigin(rect.mLeft, rect.mBottom); + mList->reshape(rect.getWidth(), rect.getHeight()); + mList->translateIntoRect(root_view_local); + + // Make sure we didn't go off bottom of screen + S32 x, y; + mList->localPointToScreen(0, 0, &x, &y); + + if (y < 0) + { + mList->translate(0, -y); + } + + // NB: this call will trigger the focuslost callback which will hide the list, so do it first + // before finally showing the list + + mList->setFocus(true); + + // Show the list and push the button down + mButton->setToggleState(true); + mList->setVisible(true); + + LLUI::getInstance()->addPopup(this); + + setUseBoundingRect(true); +// updateBoundingRect(); +} + +void LLComboBox::hideList() +{ + if (mList->getVisible()) + { + // assert selection in list + if(mAllowNewValues) + { + // mLastSelectedIndex = -1 means that we entered a new value, don't select + // any of existing items in this case. + if(mLastSelectedIndex >= 0) + mList->selectNthItem(mLastSelectedIndex); + } + else if(mLastSelectedIndex >= 0) + mList->selectNthItem(mLastSelectedIndex); + + mButton->setToggleState(false); + mList->setVisible(false); + mList->mouseOverHighlightNthItem(-1); + + setUseBoundingRect(false); + LLUI::getInstance()->removePopup(this); +// updateBoundingRect(); + } +} + +void LLComboBox::onButtonMouseDown() +{ + if (!mList->getVisible()) + { + // this might change selection, so do it first + prearrangeList(); + + // highlight the last selected item from the original selection before potentially selecting a new item + // as visual cue to original value of combo box + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) + { + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); + } + + if (mList->getItemCount() != 0) + { + showList(); + } + + setFocus( true ); + + // pass mouse capture on to list if button is depressed + if (mButton->hasMouseCapture()) + { + gFocusMgr.setMouseCapture(mList); + + // But keep the "pressed" look, which buttons normally lose when they + // lose focus + mButton->setForcePressedState(true); + } + } + else + { + hideList(); + } + +} + +void LLComboBox::onListMouseUp() +{ + // In some cases this is the termination of a mouse click that started on + // the button, so clear its pressed state + mButton->setForcePressedState(false); +} + +//------------------------------------------------------------------ +// static functions +//------------------------------------------------------------------ + +void LLComboBox::onItemSelected(const LLSD& data) +{ + mLastSelectedIndex = getCurrentIndex(); + if (mLastSelectedIndex != -1) + { + updateLabel(); + + if (mAllowTextEntry) + { + gFocusMgr.setKeyboardFocus(mTextEntry); + mTextEntry->selectAll(); + } + } + // hiding the list reasserts the old value stored in the text editor/dropdown button + hideList(); + + // commit does the reverse, asserting the value in the list + onCommit(); +} + +bool LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) +{ + std::string tool_tip; + + if(LLUICtrl::handleToolTip(x, y, mask)) + { + return true; + } + + tool_tip = getToolTip(); + if (tool_tip.empty()) + { + tool_tip = getSelectedItemLabel(); + } + + if( !tool_tip.empty() ) + { + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(tool_tip) + .sticky_rect(calcScreenRect())); + } + return true; +} + +bool LLComboBox::handleKeyHere(KEY key, MASK mask) +{ + bool result = false; + if (hasFocus()) + { + if (mList->getVisible() + && key == KEY_ESCAPE && mask == MASK_NONE) + { + hideList(); + return true; + } + //give list a chance to pop up and handle key + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) + { + // highlight the original selection before potentially selecting a new item + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); + } + result = mList->handleKeyHere(key, mask); + + // will only see return key if it is originating from line editor + // since the dropdown button eats the key + if (key == KEY_RETURN) + { + if (mask == MASK_NONE) + { + mOnReturnSignal(this, getValue()); + } + + // don't show list and don't eat key input when committing + // free-form text entry with RETURN since user already knows + // what they are trying to select + return false; + } + // if selection has changed, pop open list + else if (mList->getLastSelectedItem() != last_selected_item + || ((key == KEY_DOWN || key == KEY_UP) + && mList->getCanSelect() + && !mList->isEmpty())) + { + showList(); + } + } + return result; +} + +bool LLComboBox::handleUnicodeCharHere(llwchar uni_char) +{ + bool result = false; + if (gFocusMgr.childHasKeyboardFocus(this)) + { + // space bar just shows the list + if (' ' != uni_char ) + { + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) + { + // highlight the original selection before potentially selecting a new item + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); + } + result = mList->handleUnicodeCharHere(uni_char); + if (mList->getLastSelectedItem() != last_selected_item) + { + showList(); + } + } + } + return result; +} + +void LLComboBox::setTextEntry(const LLStringExplicit& text) +{ + if (mTextEntry) + { + mTextEntry->setText(text); + mHasAutocompletedText = false; + updateSelection(); + } +} + +void LLComboBox::setKeystrokeOnEsc(bool enable) +{ + if (mTextEntry) + { + mTextEntry->setKeystrokeOnEsc(enable); + } +} + +void LLComboBox::onTextEntry(LLLineEditor* line_editor) +{ + if (mTextEntryCallback != NULL) + { + (mTextEntryCallback)(line_editor, LLSD()); + } + + KEY key = gKeyboard->currentKey(); + if (key == KEY_BACKSPACE || + key == KEY_DELETE) + { + if (mList->selectItemByLabel(line_editor->getText(), false)) + { + line_editor->setTentative(false); + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else + { + line_editor->setTentative(mTextEntryTentative); + mList->deselectAllItems(); + mLastSelectedIndex = -1; + } + if (mTextChangedCallback != NULL) + { + (mTextChangedCallback)(line_editor, LLSD()); + } + return; + } + + if (key == KEY_LEFT || + key == KEY_RIGHT) + { + return; + } + + if (key == KEY_DOWN) + { + setCurrentByIndex(llmin(getItemCount() - 1, getCurrentIndex() + 1)); + if (!mList->getVisible()) + { + prearrangeList(); + + if (mList->getItemCount() != 0) + { + showList(); + } + } + line_editor->selectAll(); + line_editor->setTentative(false); + } + else if (key == KEY_UP) + { + setCurrentByIndex(llmax(0, getCurrentIndex() - 1)); + if (!mList->getVisible()) + { + prearrangeList(); + + if (mList->getItemCount() != 0) + { + showList(); + } + } + line_editor->selectAll(); + line_editor->setTentative(false); + } + else + { + // RN: presumably text entry + updateSelection(); + } + if (mTextChangedCallback != NULL) + { + (mTextChangedCallback)(line_editor, LLSD()); + } +} + +void LLComboBox::updateSelection() +{ + LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor()); + // user-entered portion of string, based on assumption that any selected + // text was a result of auto-completion + LLWString user_wstring = mHasAutocompletedText ? left_wstring : mTextEntry->getWText(); + std::string full_string = mTextEntry->getText(); + + // go ahead and arrange drop down list on first typed character, even + // though we aren't showing it... some code relies on prearrange + // callback to populate content + if( mTextEntry->getWText().size() == 1 ) + { + prearrangeList(mTextEntry->getText()); + } + + if (mList->selectItemByLabel(full_string, false)) + { + mTextEntry->setTentative(false); + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else if (mList->selectItemByPrefix(left_wstring, false)) + { + LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel()); + LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size()); + mTextEntry->setText(wstring_to_utf8str(wtext)); + mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size()); + mTextEntry->endSelection(); + mTextEntry->setTentative(false); + mHasAutocompletedText = true; + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else // no matching items found + { + mList->deselectAllItems(); + mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion + mTextEntry->setTentative(mTextEntryTentative); + mHasAutocompletedText = false; + mLastSelectedIndex = -1; + } +} + +void LLComboBox::onTextCommit(const LLSD& data) +{ + std::string text = mTextEntry->getText(); + setSimple(text); + onCommit(); + mTextEntry->selectAll(); +} + +void LLComboBox::setFocus(bool b) +{ + LLUICtrl::setFocus(b); + + if (b) + { + mList->clearSearchString(); + if (mList->getVisible()) + { + mList->setFocus(true); + } + } +} + +void LLComboBox::prearrangeList(std::string filter) +{ + if (mPrearrangeCallback) + { + mPrearrangeCallback(this, LLSD(filter)); + } +} + + +//============================================================================ +// ll::ui::SearchableControl functions + +//virtual +std::string LLComboBox::_getSearchText() const +{ + std::string res; + if (mList) + { + // getAllData returns a full copy of content, might be a + // better option to implement an mList->getSearchText(column) + std::vector data = mList->getAllData(); + std::vector::iterator iter = data.begin(); + while (iter != data.end()) + { + LLScrollListCell* cell = (*iter)->getColumn(0); + if (cell) + { + std::string whitelist_url = cell->getValue().asString(); + res += cell->getValue().asString(); + } + iter++; + } + } + return res + getToolTip(); +} + +//virtual +void LLComboBox::onSetHighlight() const +{ + if (mButton) + { + mButton->ll::ui::SearchableControl::setHighlighted(ll::ui::SearchableControl::getHighlighted()); + } +} + +void LLComboBox::imageLoaded() +{ + if (mAllowTextEntry) + { + LLRect rect = getLocalRect(); + S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; + S32 shadow_size = BTN_DROP_SHADOW; + mButton->setRect(LLRect(getRect().getWidth() - llmax(8, arrow_width) - 2 * shadow_size, + rect.mTop, rect.mRight, rect.mBottom)); + if (mButton->getVisible()) + { + // recalculate field size + if (mTextEntry) + { + LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * BTN_DROP_SHADOW; + mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true); + } + } + } +} + +//============================================================================ +// LLCtrlListInterface functions + +S32 LLComboBox::getItemCount() const +{ + return mList->getItemCount(); +} + +void LLComboBox::addColumn(const LLSD& column, EAddPosition pos) +{ + mList->clearColumns(); + mList->addColumn(column, pos); +} + +void LLComboBox::clearColumns() +{ + mList->clearColumns(); +} + +void LLComboBox::setColumnLabel(const std::string& column, const std::string& label) +{ + mList->setColumnLabel(column, label); +} + +LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata) +{ + return mList->addElement(value, pos, userdata); +} + +LLScrollListItem* LLComboBox::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) +{ + return mList->addSimpleElement(value, pos, id); +} + +void LLComboBox::clearRows() +{ + mList->clearRows(); +} + +void LLComboBox::sortByColumn(const std::string& name, bool ascending) +{ + mList->sortByColumn(name, ascending); +} + +//============================================================================ +//LLCtrlSelectionInterface functions + +bool LLComboBox::setCurrentByID(const LLUUID& id) +{ + bool found = mList->selectByID( id ); + + if (found) + { + setLabel(getSelectedItemLabel()); + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + + return found; +} + +LLUUID LLComboBox::getCurrentID() const +{ + return mList->getStringUUIDSelectedItem(); +} +bool LLComboBox::setSelectedByValue(const LLSD& value, bool selected) +{ + bool found = mList->setSelectedByValue(value, selected); + if (found) + { + setLabel(getSelectedItemLabel()); + } + return found; +} + +LLSD LLComboBox::getSelectedValue() +{ + return mList->getSelectedValue(); +} + +bool LLComboBox::isSelected(const LLSD& value) const +{ + return mList->isSelected(value); +} + +bool LLComboBox::operateOnSelection(EOperation op) +{ + if (op == OP_DELETE) + { + mList->deleteSelectedItems(); + return true; + } + return false; +} + +bool LLComboBox::operateOnAll(EOperation op) +{ + if (op == OP_DELETE) + { + clearRows(); + return true; + } + return false; +} + +bool LLComboBox::selectItemRange( S32 first, S32 last ) +{ + return mList->selectItemRange(first, last); +} + + +static LLDefaultChildRegistry::Register register_icons_combo_box("icons_combo_box"); + +LLIconsComboBox::Params::Params() +: icon_column("icon_column", ICON_COLUMN), + label_column("label_column", LABEL_COLUMN) +{} + +LLIconsComboBox::LLIconsComboBox(const LLIconsComboBox::Params& p) +: LLComboBox(p), + mIconColumnIndex(p.icon_column), + mLabelColumnIndex(p.label_column) +{} + +const std::string LLIconsComboBox::getSelectedItemLabel(S32 column) const +{ + mButton->setImageOverlay(LLComboBox::getSelectedItemLabel(mIconColumnIndex), mButton->getImageOverlayHAlign()); + + return LLComboBox::getSelectedItemLabel(mLabelColumnIndex); +} diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index dd827fb548..cc1c2885fc 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -1,284 +1,284 @@ -/** - * @file llcombobox.h - * @brief LLComboBox base class - * - * $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$ - */ - -// A control that displays the name of the chosen item, which when clicked -// shows a scrolling box of choices. - -#ifndef LL_LLCOMBOBOX_H -#define LL_LLCOMBOBOX_H - -#include "llbutton.h" -#include "lluictrl.h" -#include "llctrlselectioninterface.h" -#include "llrect.h" -#include "llscrolllistctrl.h" -#include "lllineeditor.h" -#include - -// Classes - -class LLFontGL; -class LLViewBorder; - -class LLComboBox -: public LLUICtrl -, public LLCtrlListInterface -, public ll::ui::SearchableControl -{ -public: - typedef enum e_preferred_position - { - ABOVE, - BELOW - } EPreferredPosition; - - struct PreferredPositionValues : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - - struct ItemParams : public LLInitParam::Block - { - Optional label; - ItemParams(); - }; - - struct Params - : public LLInitParam::Block - { - Optional allow_text_entry, - show_text_as_tentative, - allow_new_values; - Optional max_chars; - Optional prearrange_callback, - text_entry_callback, - text_changed_callback; - - Optional list_position; - - // components - Optional combo_button; - Optional combo_list; - Optional combo_editor; - - Optional drop_down_button; - - Multiple items; - - Params(); - }; - - - virtual ~LLComboBox(); - /*virtual*/ bool postBuild(); - -protected: - friend class LLUICtrlFactory; - LLComboBox(const Params&); - void initFromParams(const Params&); - void prearrangeList(std::string filter = ""); - - virtual std::string _getSearchText() const; - virtual void onSetHighlight() const; - - void imageLoaded(); - -public: - // LLView interface - virtual void onFocusLost(); - - virtual bool handleToolTip(S32 x, S32 y, MASK mask); - virtual bool handleKeyHere(KEY key, MASK mask); - virtual bool handleUnicodeCharHere(llwchar uni_char); - - // LLUICtrl interface - virtual void clear(); // select nothing - virtual void onCommit(); - virtual bool acceptsTextInput() const { return mAllowTextEntry; } - virtual bool isDirty() const; // Returns true if the user has modified this control. - virtual void resetDirty(); // Clear dirty state - - virtual void setFocus(bool b); - - // Selects item by underlying LLSD value, using LLSD::asString() matching. - // For simple items, this is just the name of the label. - virtual void setValue(const LLSD& value ); - - // Gets underlying LLSD value for currently selected items. For simple - // items, this is just the label. - virtual LLSD getValue() const; - - void setTextEntry(const LLStringExplicit& text); - void setKeystrokeOnEsc(bool enable); - - LLScrollListItem* add(const std::string& name, EAddPosition pos = ADD_BOTTOM, bool enabled = true); // add item "name" to menu - LLScrollListItem* add(const std::string& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true); - LLScrollListItem* add(const std::string& name, void* userdata, EAddPosition pos = ADD_BOTTOM, bool enabled = true); - LLScrollListItem* add(const std::string& name, LLSD value, EAddPosition pos = ADD_BOTTOM, bool enabled = true); - LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM); - bool remove( S32 index ); // remove item by index, return true if found and removed - void removeall() { clearRows(); } - bool itemExists(const std::string& name); - - void sortByName(bool ascending = true); // Sort the entries in the combobox by name - - // Select current item by name using selectItemByLabel. Returns false if not found. - bool setSimple(const LLStringExplicit& name); - // Get name of current item. Returns an empty string if not found. - const std::string getSimple() const; - // Get contents of column x of selected row - virtual const std::string getSelectedItemLabel(S32 column = 0) const; - - // Sets the label, which doesn't have to exist in the label. - // This is probably a UI abuse. - void setLabel(const LLStringExplicit& name); - - // Updates the combobox label to match the selected list item. - void updateLabel(); - - bool remove(const std::string& name); // remove item "name", return true if found and removed - - bool setCurrentByIndex( S32 index ); - S32 getCurrentIndex() const; - - void setEnabledByValue(const LLSD& value, bool enabled); - - void createLineEditor(const Params&); - - //======================================================================== - LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }; - LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; }; - - // LLCtrlListInterface functions - // See llscrolllistctrl.h - virtual S32 getItemCount() const; - // Overwrites the default column (See LLScrollListCtrl for format) - 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 = ADD_BOTTOM, const LLSD& id = LLSD()); - virtual void clearRows(); - virtual void sortByColumn(const std::string& name, bool ascending); - - // LLCtrlSelectionInterface functions - virtual bool getCanSelect() const { return true; } - virtual bool selectFirstItem() { return setCurrentByIndex(0); } - virtual bool selectNthItem( S32 index ) { return setCurrentByIndex(index); } - virtual bool selectItemRange( S32 first, S32 last ); - virtual S32 getFirstSelectedIndex() const { return getCurrentIndex(); } - virtual bool setCurrentByID( const LLUUID& id ); - virtual LLUUID getCurrentID() const; // LLUUID::null if no items in menu - virtual bool setSelectedByValue(const LLSD& value, bool selected); - virtual LLSD getSelectedValue(); - virtual bool isSelected(const LLSD& value) const; - virtual bool operateOnSelection(EOperation op); - virtual bool operateOnAll(EOperation op); - - //======================================================================== - - void setLeftTextPadding(S32 pad); - - void* getCurrentUserdata(); - - void setPrearrangeCallback( commit_callback_t cb ) { mPrearrangeCallback = cb; } - void setTextEntryCallback( commit_callback_t cb ) { mTextEntryCallback = cb; } - void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } - - /** - * Connects callback to signal called when Return key is pressed. - */ - boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); } - - void setButtonVisible(bool visible); - - void onButtonMouseDown(); - void onListMouseUp(); - void onItemSelected(const LLSD& data); - void onTextCommit(const LLSD& data); - - void updateSelection(); - virtual void showList(); - virtual void hideList(); - - virtual void onTextEntry(LLLineEditor* line_editor); - -protected: - LLButton* mButton; - LLLineEditor* mTextEntry; - LLScrollListCtrl* mList; - EPreferredPosition mListPosition; - LLPointer mArrowImage; - LLUIString mLabel; - bool mHasAutocompletedText; - -private: - bool mAllowTextEntry; - bool mAllowNewValues; - S32 mMaxChars; - bool mTextEntryTentative; - commit_callback_t mPrearrangeCallback; - commit_callback_t mTextEntryCallback; - commit_callback_t mTextChangedCallback; - commit_callback_t mSelectionCallback; - boost::signals2::connection mTopLostSignalConnection; - boost::signals2::connection mImageLoadedConnection; - commit_signal_t mOnReturnSignal; - S32 mLastSelectedIndex; -}; - -// A combo box with icons for the list of items. -class LLIconsComboBox -: public LLComboBox -{ -public: - struct Params - : public LLInitParam::Block - { - Optional icon_column, - label_column; - Params(); - }; - - /*virtual*/ const std::string getSelectedItemLabel(S32 column = 0) const; - -private: - enum EColumnIndex - { - ICON_COLUMN = 0, - LABEL_COLUMN - }; - - friend class LLUICtrlFactory; - LLIconsComboBox(const Params&); - virtual ~LLIconsComboBox() {}; - - S32 mIconColumnIndex; - S32 mLabelColumnIndex; -}; - -#endif +/** + * @file llcombobox.h + * @brief LLComboBox base class + * + * $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$ + */ + +// A control that displays the name of the chosen item, which when clicked +// shows a scrolling box of choices. + +#ifndef LL_LLCOMBOBOX_H +#define LL_LLCOMBOBOX_H + +#include "llbutton.h" +#include "lluictrl.h" +#include "llctrlselectioninterface.h" +#include "llrect.h" +#include "llscrolllistctrl.h" +#include "lllineeditor.h" +#include + +// Classes + +class LLFontGL; +class LLViewBorder; + +class LLComboBox +: public LLUICtrl +, public LLCtrlListInterface +, public ll::ui::SearchableControl +{ +public: + typedef enum e_preferred_position + { + ABOVE, + BELOW + } EPreferredPosition; + + struct PreferredPositionValues : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + + struct ItemParams : public LLInitParam::Block + { + Optional label; + ItemParams(); + }; + + struct Params + : public LLInitParam::Block + { + Optional allow_text_entry, + show_text_as_tentative, + allow_new_values; + Optional max_chars; + Optional prearrange_callback, + text_entry_callback, + text_changed_callback; + + Optional list_position; + + // components + Optional combo_button; + Optional combo_list; + Optional combo_editor; + + Optional drop_down_button; + + Multiple items; + + Params(); + }; + + + virtual ~LLComboBox(); + /*virtual*/ bool postBuild(); + +protected: + friend class LLUICtrlFactory; + LLComboBox(const Params&); + void initFromParams(const Params&); + void prearrangeList(std::string filter = ""); + + virtual std::string _getSearchText() const; + virtual void onSetHighlight() const; + + void imageLoaded(); + +public: + // LLView interface + virtual void onFocusLost(); + + virtual bool handleToolTip(S32 x, S32 y, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); + virtual bool handleUnicodeCharHere(llwchar uni_char); + + // LLUICtrl interface + virtual void clear(); // select nothing + virtual void onCommit(); + virtual bool acceptsTextInput() const { return mAllowTextEntry; } + virtual bool isDirty() const; // Returns true if the user has modified this control. + virtual void resetDirty(); // Clear dirty state + + virtual void setFocus(bool b); + + // Selects item by underlying LLSD value, using LLSD::asString() matching. + // For simple items, this is just the name of the label. + virtual void setValue(const LLSD& value ); + + // Gets underlying LLSD value for currently selected items. For simple + // items, this is just the label. + virtual LLSD getValue() const; + + void setTextEntry(const LLStringExplicit& text); + void setKeystrokeOnEsc(bool enable); + + LLScrollListItem* add(const std::string& name, EAddPosition pos = ADD_BOTTOM, bool enabled = true); // add item "name" to menu + LLScrollListItem* add(const std::string& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true); + LLScrollListItem* add(const std::string& name, void* userdata, EAddPosition pos = ADD_BOTTOM, bool enabled = true); + LLScrollListItem* add(const std::string& name, LLSD value, EAddPosition pos = ADD_BOTTOM, bool enabled = true); + LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM); + bool remove( S32 index ); // remove item by index, return true if found and removed + void removeall() { clearRows(); } + bool itemExists(const std::string& name); + + void sortByName(bool ascending = true); // Sort the entries in the combobox by name + + // Select current item by name using selectItemByLabel. Returns false if not found. + bool setSimple(const LLStringExplicit& name); + // Get name of current item. Returns an empty string if not found. + const std::string getSimple() const; + // Get contents of column x of selected row + virtual const std::string getSelectedItemLabel(S32 column = 0) const; + + // Sets the label, which doesn't have to exist in the label. + // This is probably a UI abuse. + void setLabel(const LLStringExplicit& name); + + // Updates the combobox label to match the selected list item. + void updateLabel(); + + bool remove(const std::string& name); // remove item "name", return true if found and removed + + bool setCurrentByIndex( S32 index ); + S32 getCurrentIndex() const; + + void setEnabledByValue(const LLSD& value, bool enabled); + + void createLineEditor(const Params&); + + //======================================================================== + LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }; + LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; }; + + // LLCtrlListInterface functions + // See llscrolllistctrl.h + virtual S32 getItemCount() const; + // Overwrites the default column (See LLScrollListCtrl for format) + 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 = ADD_BOTTOM, const LLSD& id = LLSD()); + virtual void clearRows(); + virtual void sortByColumn(const std::string& name, bool ascending); + + // LLCtrlSelectionInterface functions + virtual bool getCanSelect() const { return true; } + virtual bool selectFirstItem() { return setCurrentByIndex(0); } + virtual bool selectNthItem( S32 index ) { return setCurrentByIndex(index); } + virtual bool selectItemRange( S32 first, S32 last ); + virtual S32 getFirstSelectedIndex() const { return getCurrentIndex(); } + virtual bool setCurrentByID( const LLUUID& id ); + virtual LLUUID getCurrentID() const; // LLUUID::null if no items in menu + virtual bool setSelectedByValue(const LLSD& value, bool selected); + virtual LLSD getSelectedValue(); + virtual bool isSelected(const LLSD& value) const; + virtual bool operateOnSelection(EOperation op); + virtual bool operateOnAll(EOperation op); + + //======================================================================== + + void setLeftTextPadding(S32 pad); + + void* getCurrentUserdata(); + + void setPrearrangeCallback( commit_callback_t cb ) { mPrearrangeCallback = cb; } + void setTextEntryCallback( commit_callback_t cb ) { mTextEntryCallback = cb; } + void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } + + /** + * Connects callback to signal called when Return key is pressed. + */ + boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); } + + void setButtonVisible(bool visible); + + void onButtonMouseDown(); + void onListMouseUp(); + void onItemSelected(const LLSD& data); + void onTextCommit(const LLSD& data); + + void updateSelection(); + virtual void showList(); + virtual void hideList(); + + virtual void onTextEntry(LLLineEditor* line_editor); + +protected: + LLButton* mButton; + LLLineEditor* mTextEntry; + LLScrollListCtrl* mList; + EPreferredPosition mListPosition; + LLPointer mArrowImage; + LLUIString mLabel; + bool mHasAutocompletedText; + +private: + bool mAllowTextEntry; + bool mAllowNewValues; + S32 mMaxChars; + bool mTextEntryTentative; + commit_callback_t mPrearrangeCallback; + commit_callback_t mTextEntryCallback; + commit_callback_t mTextChangedCallback; + commit_callback_t mSelectionCallback; + boost::signals2::connection mTopLostSignalConnection; + boost::signals2::connection mImageLoadedConnection; + commit_signal_t mOnReturnSignal; + S32 mLastSelectedIndex; +}; + +// A combo box with icons for the list of items. +class LLIconsComboBox +: public LLComboBox +{ +public: + struct Params + : public LLInitParam::Block + { + Optional icon_column, + label_column; + Params(); + }; + + /*virtual*/ const std::string getSelectedItemLabel(S32 column = 0) const; + +private: + enum EColumnIndex + { + ICON_COLUMN = 0, + LABEL_COLUMN + }; + + friend class LLUICtrlFactory; + LLIconsComboBox(const Params&); + virtual ~LLIconsComboBox() {}; + + S32 mIconColumnIndex; + S32 mLabelColumnIndex; +}; + +#endif diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 4c163b10b0..be100a6fd2 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -1,403 +1,403 @@ -/** - * @file llconsole.cpp - * @brief a scrolling console output device - * - * $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 "linden_common.h" - -#include "llconsole.h" - -// linden library includes -#include "llmath.h" -//#include "llviewercontrol.h" -#include "llcriticaldamp.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llui.h" -#include "lluiimage.h" -//#include "llviewerimage.h" -//#include "llviewerimagelist.h" -//#include "llviewerwindow.h" -#include "llsd.h" -#include "llfontgl.h" -#include "llmath.h" - -//#include "llstartup.h" - -// Used for LCD display -extern void AddNewDebugConsoleToLCD(const LLWString &newLine); - -LLConsole* gConsole = NULL; // Created and destroyed in LLViewerWindow. - -const F32 FADE_DURATION = 2.f; - -static LLDefaultChildRegistry::Register r("console"); - -LLConsole::LLConsole(const LLConsole::Params& p) -: LLUICtrl(p), - LLFixedBuffer(p.max_lines), - mLinePersistTime(p.persist_time), // seconds - mFont(p.font), - mConsoleWidth(0), - mConsoleHeight(0) -{ - if (p.font_size_index.isProvided()) - { - setFontSize(p.font_size_index); - } - mFadeTime = mLinePersistTime - FADE_DURATION; - setMaxLines(LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines")); -} - -void LLConsole::setLinePersistTime(F32 seconds) -{ - mLinePersistTime = seconds; - mFadeTime = mLinePersistTime - FADE_DURATION; -} - -void LLConsole::reshape(S32 width, S32 height, bool called_from_parent) -{ - S32 new_width = llmax(50, llmin(getRect().getWidth(), width)); - S32 new_height = llmax(llfloor(mFont->getLineHeight()) + 15, llmin(getRect().getHeight(), height)); - - if ( mConsoleWidth == new_width - && mConsoleHeight == new_height ) - { - return; - } - - mConsoleWidth = new_width; - mConsoleHeight= new_height; - - LLUICtrl::reshape(new_width, new_height, called_from_parent); - - for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++) - { - (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true); - } -} - -void LLConsole::setFontSize(S32 size_index) -{ - if (-1 == size_index) - { - mFont = LLFontGL::getFontMonospace(); - } - else if (0 == size_index) - { - mFont = LLFontGL::getFontSansSerif(); - } - else if (1 == size_index) - { - mFont = LLFontGL::getFontSansSerifBig(); - } - else - { - mFont = LLFontGL::getFontSansSerifHuge(); - } - // Make sure the font exists - if (mFont == NULL) - { - mFont = LLFontGL::getFontDefault(); - } - - for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++) - { - (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true); - } -} - -void LLConsole::draw() -{ - // Units in pixels - static const F32 padding_horizontal = 10; - static const F32 padding_vertical = 3; - LLGLSUIDefault gls_ui; - - // skip lines added more than mLinePersistTime ago - F32 cur_time = mTimer.getElapsedTimeF32(); - - F32 skip_time = cur_time - mLinePersistTime; - F32 fade_time = cur_time - mFadeTime; - - if (mParagraphs.empty()) //No text to draw. - { - return; - } - - U32 num_lines=0; - - paragraph_t::reverse_iterator paragraph_it; - paragraph_it = mParagraphs.rbegin(); - U32 paragraph_num=mParagraphs.size(); - - while (!mParagraphs.empty() && paragraph_it != mParagraphs.rend()) - { - num_lines += (*paragraph_it).mLines.size(); - if(num_lines > mMaxLines - || ( (mLinePersistTime > (F32)0.f) && ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime) <= (F32)0.f)) - { //All lines above here are done. Lose them. - for (U32 i=0;i console_bg_opacity(*LLUI::getInstance()->mSettingGroups["config"], "ConsoleBackgroundOpacity", 0.7f); - F32 console_opacity = llclamp(console_bg_opacity(), 0.f, 1.f); - - LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground"); - color.mV[VALPHA] *= console_opacity; - - F32 line_height = mFont->getLineHeight(); - - for(paragraph_it = mParagraphs.rbegin(); paragraph_it != mParagraphs.rend(); paragraph_it++) - { - S32 target_height = llfloor( (*paragraph_it).mLines.size() * line_height + padding_vertical); - S32 target_width = llfloor( (*paragraph_it).mMaxWidth + padding_horizontal); - - y_pos += ((*paragraph_it).mLines.size()) * line_height; - imagep->drawSolid(-14, (S32)(y_pos + line_height - target_height), target_width, target_height, color); - - F32 y_off=0; - - F32 alpha; - - if ((mLinePersistTime > 0.f) && ((*paragraph_it).mAddTime < fade_time)) - { - alpha = ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime); - } - else - { - alpha = 1.0f; - } - - if( alpha > 0.f ) - { - for (lines_t::iterator line_it=(*paragraph_it).mLines.begin(); - line_it != (*paragraph_it).mLines.end(); - line_it ++) - { - for (line_color_segments_t::iterator seg_it = (*line_it).mLineColorSegments.begin(); - seg_it != (*line_it).mLineColorSegments.end(); - seg_it++) - { - mFont->render((*seg_it).mText, 0, (*seg_it).mXPosition - 8, y_pos - y_off, - LLColor4( - (*seg_it).mColor.mV[VRED], - (*seg_it).mColor.mV[VGREEN], - (*seg_it).mColor.mV[VBLUE], - (*seg_it).mColor.mV[VALPHA]*alpha), - LLFontGL::LEFT, - LLFontGL::BASELINE, - LLFontGL::NORMAL, - LLFontGL::DROP_SHADOW, - S32_MAX, - target_width - ); - } - y_off += line_height; - } - } - y_pos += padding_vertical; - } -} - -//Generate highlight color segments for this paragraph. Pass in default color of paragraph. -void LLConsole::Paragraph::makeParagraphColorSegments (const LLColor4 &color) -{ - LLSD paragraph_color_segments; - paragraph_color_segments[0]["text"] =wstring_to_utf8str(mParagraphText); - LLSD color_sd = color.getValue(); - paragraph_color_segments[0]["color"]=color_sd; - - for(LLSD::array_const_iterator color_segment_it = paragraph_color_segments.beginArray(); - color_segment_it != paragraph_color_segments.endArray(); - ++color_segment_it) - { - LLSD color_llsd = (*color_segment_it)["color"]; - std::string color_str = (*color_segment_it)["text"].asString(); - - ParagraphColorSegment color_segment; - - color_segment.mColor.setValue(color_llsd); - color_segment.mNumChars = color_str.length(); - - mParagraphColorSegments.push_back(color_segment); - } -} - -//Called when a paragraph is added to the console or window is resized. -void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, bool force_resize) -{ - if ( !force_resize ) - { - if ( mMaxWidth >= 0.0f - && mMaxWidth < screen_width ) - { - return; //No resize required. - } - } - - screen_width = screen_width - 30; //Margin for small windows. - - if ( mParagraphText.empty() - || mParagraphColorSegments.empty() - || font == NULL) - { - return; //Not enough info to complete. - } - - mLines.clear(); //Chuck everything. - mMaxWidth = 0.0f; - - paragraph_color_segments_t::iterator current_color = mParagraphColorSegments.begin(); - U32 current_color_length = (*current_color).mNumChars; - - S32 paragraph_offset = 0; //Offset into the paragraph text. - - // Wrap lines that are longer than the view is wide. - while( paragraph_offset < (S32)mParagraphText.length() && - mParagraphText[paragraph_offset] != 0) - { - S32 skip_chars; // skip '\n' - // Figure out if a word-wrapped line fits here. - LLWString::size_type line_end = mParagraphText.find_first_of(llwchar('\n'), paragraph_offset); - if (line_end != LLWString::npos) - { - skip_chars = 1; // skip '\n' - } - else - { - line_end = mParagraphText.size(); - skip_chars = 0; - } - - U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); - - if (drawable != 0) - { - F32 x_position = 0; //Screen X position of text. - - mMaxWidth = llmax( mMaxWidth, (F32)font->getWidth( mParagraphText.substr( paragraph_offset, drawable ).c_str() ) ); - Line line; - - U32 left_to_draw = drawable; - U32 drawn = 0; - - while (left_to_draw >= current_color_length - && current_color != mParagraphColorSegments.end() ) - { - LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, current_color_length ); - line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line. - (*current_color).mColor, - x_position ) ); - - x_position += font->getWidth( color_text.c_str() ); //Set up next screen position. - - drawn += current_color_length; - left_to_draw -= current_color_length; - - current_color++; //Goto next paragraph color record. - - if (current_color != mParagraphColorSegments.end()) - { - current_color_length = (*current_color).mNumChars; - } - } - - if (left_to_draw > 0 && current_color != mParagraphColorSegments.end() ) - { - LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, left_to_draw ); - - line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line. - (*current_color).mColor, - x_position ) ); - - current_color_length -= left_to_draw; - } - mLines.push_back(line); //Append line to paragraph line list. - } - paragraph_offset += (drawable + skip_chars); - } -} - -//Pass in the string and the default color for this block of text. -LLConsole::Paragraph::Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width) -: mParagraphText(str), mAddTime(add_time), mMaxWidth(-1) -{ - makeParagraphColorSegments(color); - updateLines( screen_width, font ); -} - -// called once per frame regardless of console visibility -// static -void LLConsole::updateClass() -{ - for (auto& con : instance_snapshot()) - { - con.update(); - } -} - -void LLConsole::update() -{ - { - LLMutexLock lock(&mMutex); - - while (!mLines.empty()) - { - mParagraphs.push_back( - Paragraph( mLines.front(), - LLColor4::white, - mTimer.getElapsedTimeF32(), - mFont, - (F32)getRect().getWidth())); - mLines.pop_front(); - } - } - - // remove old paragraphs which can't possibly be visible any more. ::draw() will do something similar but more conservative - we do this here because ::draw() isn't guaranteed to ever be called! (i.e. the console isn't visible) - while ((S32)mParagraphs.size() > llmax((S32)0, (S32)(mMaxLines))) - { - mParagraphs.pop_front(); - } -} - +/** + * @file llconsole.cpp + * @brief a scrolling console output device + * + * $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 "linden_common.h" + +#include "llconsole.h" + +// linden library includes +#include "llmath.h" +//#include "llviewercontrol.h" +#include "llcriticaldamp.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llui.h" +#include "lluiimage.h" +//#include "llviewerimage.h" +//#include "llviewerimagelist.h" +//#include "llviewerwindow.h" +#include "llsd.h" +#include "llfontgl.h" +#include "llmath.h" + +//#include "llstartup.h" + +// Used for LCD display +extern void AddNewDebugConsoleToLCD(const LLWString &newLine); + +LLConsole* gConsole = NULL; // Created and destroyed in LLViewerWindow. + +const F32 FADE_DURATION = 2.f; + +static LLDefaultChildRegistry::Register r("console"); + +LLConsole::LLConsole(const LLConsole::Params& p) +: LLUICtrl(p), + LLFixedBuffer(p.max_lines), + mLinePersistTime(p.persist_time), // seconds + mFont(p.font), + mConsoleWidth(0), + mConsoleHeight(0) +{ + if (p.font_size_index.isProvided()) + { + setFontSize(p.font_size_index); + } + mFadeTime = mLinePersistTime - FADE_DURATION; + setMaxLines(LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines")); +} + +void LLConsole::setLinePersistTime(F32 seconds) +{ + mLinePersistTime = seconds; + mFadeTime = mLinePersistTime - FADE_DURATION; +} + +void LLConsole::reshape(S32 width, S32 height, bool called_from_parent) +{ + S32 new_width = llmax(50, llmin(getRect().getWidth(), width)); + S32 new_height = llmax(llfloor(mFont->getLineHeight()) + 15, llmin(getRect().getHeight(), height)); + + if ( mConsoleWidth == new_width + && mConsoleHeight == new_height ) + { + return; + } + + mConsoleWidth = new_width; + mConsoleHeight= new_height; + + LLUICtrl::reshape(new_width, new_height, called_from_parent); + + for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++) + { + (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true); + } +} + +void LLConsole::setFontSize(S32 size_index) +{ + if (-1 == size_index) + { + mFont = LLFontGL::getFontMonospace(); + } + else if (0 == size_index) + { + mFont = LLFontGL::getFontSansSerif(); + } + else if (1 == size_index) + { + mFont = LLFontGL::getFontSansSerifBig(); + } + else + { + mFont = LLFontGL::getFontSansSerifHuge(); + } + // Make sure the font exists + if (mFont == NULL) + { + mFont = LLFontGL::getFontDefault(); + } + + for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++) + { + (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true); + } +} + +void LLConsole::draw() +{ + // Units in pixels + static const F32 padding_horizontal = 10; + static const F32 padding_vertical = 3; + LLGLSUIDefault gls_ui; + + // skip lines added more than mLinePersistTime ago + F32 cur_time = mTimer.getElapsedTimeF32(); + + F32 skip_time = cur_time - mLinePersistTime; + F32 fade_time = cur_time - mFadeTime; + + if (mParagraphs.empty()) //No text to draw. + { + return; + } + + U32 num_lines=0; + + paragraph_t::reverse_iterator paragraph_it; + paragraph_it = mParagraphs.rbegin(); + U32 paragraph_num=mParagraphs.size(); + + while (!mParagraphs.empty() && paragraph_it != mParagraphs.rend()) + { + num_lines += (*paragraph_it).mLines.size(); + if(num_lines > mMaxLines + || ( (mLinePersistTime > (F32)0.f) && ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime) <= (F32)0.f)) + { //All lines above here are done. Lose them. + for (U32 i=0;i console_bg_opacity(*LLUI::getInstance()->mSettingGroups["config"], "ConsoleBackgroundOpacity", 0.7f); + F32 console_opacity = llclamp(console_bg_opacity(), 0.f, 1.f); + + LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground"); + color.mV[VALPHA] *= console_opacity; + + F32 line_height = mFont->getLineHeight(); + + for(paragraph_it = mParagraphs.rbegin(); paragraph_it != mParagraphs.rend(); paragraph_it++) + { + S32 target_height = llfloor( (*paragraph_it).mLines.size() * line_height + padding_vertical); + S32 target_width = llfloor( (*paragraph_it).mMaxWidth + padding_horizontal); + + y_pos += ((*paragraph_it).mLines.size()) * line_height; + imagep->drawSolid(-14, (S32)(y_pos + line_height - target_height), target_width, target_height, color); + + F32 y_off=0; + + F32 alpha; + + if ((mLinePersistTime > 0.f) && ((*paragraph_it).mAddTime < fade_time)) + { + alpha = ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime); + } + else + { + alpha = 1.0f; + } + + if( alpha > 0.f ) + { + for (lines_t::iterator line_it=(*paragraph_it).mLines.begin(); + line_it != (*paragraph_it).mLines.end(); + line_it ++) + { + for (line_color_segments_t::iterator seg_it = (*line_it).mLineColorSegments.begin(); + seg_it != (*line_it).mLineColorSegments.end(); + seg_it++) + { + mFont->render((*seg_it).mText, 0, (*seg_it).mXPosition - 8, y_pos - y_off, + LLColor4( + (*seg_it).mColor.mV[VRED], + (*seg_it).mColor.mV[VGREEN], + (*seg_it).mColor.mV[VBLUE], + (*seg_it).mColor.mV[VALPHA]*alpha), + LLFontGL::LEFT, + LLFontGL::BASELINE, + LLFontGL::NORMAL, + LLFontGL::DROP_SHADOW, + S32_MAX, + target_width + ); + } + y_off += line_height; + } + } + y_pos += padding_vertical; + } +} + +//Generate highlight color segments for this paragraph. Pass in default color of paragraph. +void LLConsole::Paragraph::makeParagraphColorSegments (const LLColor4 &color) +{ + LLSD paragraph_color_segments; + paragraph_color_segments[0]["text"] =wstring_to_utf8str(mParagraphText); + LLSD color_sd = color.getValue(); + paragraph_color_segments[0]["color"]=color_sd; + + for(LLSD::array_const_iterator color_segment_it = paragraph_color_segments.beginArray(); + color_segment_it != paragraph_color_segments.endArray(); + ++color_segment_it) + { + LLSD color_llsd = (*color_segment_it)["color"]; + std::string color_str = (*color_segment_it)["text"].asString(); + + ParagraphColorSegment color_segment; + + color_segment.mColor.setValue(color_llsd); + color_segment.mNumChars = color_str.length(); + + mParagraphColorSegments.push_back(color_segment); + } +} + +//Called when a paragraph is added to the console or window is resized. +void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, bool force_resize) +{ + if ( !force_resize ) + { + if ( mMaxWidth >= 0.0f + && mMaxWidth < screen_width ) + { + return; //No resize required. + } + } + + screen_width = screen_width - 30; //Margin for small windows. + + if ( mParagraphText.empty() + || mParagraphColorSegments.empty() + || font == NULL) + { + return; //Not enough info to complete. + } + + mLines.clear(); //Chuck everything. + mMaxWidth = 0.0f; + + paragraph_color_segments_t::iterator current_color = mParagraphColorSegments.begin(); + U32 current_color_length = (*current_color).mNumChars; + + S32 paragraph_offset = 0; //Offset into the paragraph text. + + // Wrap lines that are longer than the view is wide. + while( paragraph_offset < (S32)mParagraphText.length() && + mParagraphText[paragraph_offset] != 0) + { + S32 skip_chars; // skip '\n' + // Figure out if a word-wrapped line fits here. + LLWString::size_type line_end = mParagraphText.find_first_of(llwchar('\n'), paragraph_offset); + if (line_end != LLWString::npos) + { + skip_chars = 1; // skip '\n' + } + else + { + line_end = mParagraphText.size(); + skip_chars = 0; + } + + U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); + + if (drawable != 0) + { + F32 x_position = 0; //Screen X position of text. + + mMaxWidth = llmax( mMaxWidth, (F32)font->getWidth( mParagraphText.substr( paragraph_offset, drawable ).c_str() ) ); + Line line; + + U32 left_to_draw = drawable; + U32 drawn = 0; + + while (left_to_draw >= current_color_length + && current_color != mParagraphColorSegments.end() ) + { + LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, current_color_length ); + line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line. + (*current_color).mColor, + x_position ) ); + + x_position += font->getWidth( color_text.c_str() ); //Set up next screen position. + + drawn += current_color_length; + left_to_draw -= current_color_length; + + current_color++; //Goto next paragraph color record. + + if (current_color != mParagraphColorSegments.end()) + { + current_color_length = (*current_color).mNumChars; + } + } + + if (left_to_draw > 0 && current_color != mParagraphColorSegments.end() ) + { + LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, left_to_draw ); + + line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line. + (*current_color).mColor, + x_position ) ); + + current_color_length -= left_to_draw; + } + mLines.push_back(line); //Append line to paragraph line list. + } + paragraph_offset += (drawable + skip_chars); + } +} + +//Pass in the string and the default color for this block of text. +LLConsole::Paragraph::Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width) +: mParagraphText(str), mAddTime(add_time), mMaxWidth(-1) +{ + makeParagraphColorSegments(color); + updateLines( screen_width, font ); +} + +// called once per frame regardless of console visibility +// static +void LLConsole::updateClass() +{ + for (auto& con : instance_snapshot()) + { + con.update(); + } +} + +void LLConsole::update() +{ + { + LLMutexLock lock(&mMutex); + + while (!mLines.empty()) + { + mParagraphs.push_back( + Paragraph( mLines.front(), + LLColor4::white, + mTimer.getElapsedTimeF32(), + mFont, + (F32)getRect().getWidth())); + mLines.pop_front(); + } + } + + // remove old paragraphs which can't possibly be visible any more. ::draw() will do something similar but more conservative - we do this here because ::draw() isn't guaranteed to ever be called! (i.e. the console isn't visible) + while ((S32)mParagraphs.size() > llmax((S32)0, (S32)(mMaxLines))) + { + mParagraphs.pop_front(); + } +} + diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h index 86ad1cc2cb..cc9fd71d84 100644 --- a/indra/llui/llconsole.h +++ b/indra/llui/llconsole.h @@ -1,156 +1,156 @@ -/** - * @file llconsole.h - * @brief a simple console-style output device - * - * $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$ - */ - -#ifndef LL_LLCONSOLE_H -#define LL_LLCONSOLE_H - -#include "llfixedbuffer.h" -#include "lluictrl.h" -#include "v4color.h" -#include - -class LLSD; - -class LLConsole : public LLFixedBuffer, public LLUICtrl, public LLInstanceTracker -{ -public: - - typedef enum e_font_size - { - MONOSPACE = -1, - SMALL = 0, - BIG = 1 - } EFontSize; - - struct Params : public LLInitParam::Block - { - Optional max_lines; - Optional persist_time; - Optional font_size_index; - Params() - : max_lines("max_lines", LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines")), - persist_time("persist_time", 0.f), // forever - font_size_index("font_size_index") - { - changeDefault(mouse_opaque, false); - } - }; -protected: - LLConsole(const Params&); - friend class LLUICtrlFactory; - -public: - // call once per frame to pull data out of LLFixedBuffer - static void updateClass(); - - //A paragraph color segment defines the color of text in a line - //of text that was received for console display. It has no - //notion of line wraps, screen position, or the text it contains. - //It is only the number of characters that are a color and the - //color. - struct ParagraphColorSegment - { - S32 mNumChars; - LLColor4 mColor; - }; - - //A line color segment is a chunk of text, the color associated - //with it, and the X Position it was calculated to begin at - //on the screen. X Positions are re-calculated if the - //screen changes size. - class LineColorSegment - { - public: - LineColorSegment(LLWString text, LLColor4 color, F32 xpos) : mText(text), mColor(color), mXPosition(xpos) {} - public: - LLWString mText; - LLColor4 mColor; - F32 mXPosition; - }; - - typedef std::list line_color_segments_t; - - //A line is composed of one or more color segments. - class Line - { - public: - line_color_segments_t mLineColorSegments; - }; - - typedef std::list lines_t; - typedef std::list paragraph_color_segments_t; - - //A paragraph is a processed element containing the entire text of the - //message (used for recalculating positions on screen resize) - //The time this message was added to the console output - //The visual screen width of the longest line in this block - //And a list of one or more lines which are used to display this message. - class Paragraph - { - public: - Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width); - void makeParagraphColorSegments ( const LLColor4 &color); - void updateLines ( F32 screen_width, const LLFontGL* font, bool force_resize=false ); - public: - LLWString mParagraphText; //The entire text of the paragraph - paragraph_color_segments_t mParagraphColorSegments; - F32 mAddTime; //Time this paragraph was added to the display. - F32 mMaxWidth; //Width of the widest line of text in this paragraph. - lines_t mLines; - - }; - - //The console contains a deque of paragraphs which represent the individual messages. - typedef std::deque paragraph_t; - paragraph_t mParagraphs; - - ~LLConsole(){}; - - // each line lasts this long after being added - void setLinePersistTime(F32 seconds); - - void reshape(S32 width, S32 height, bool called_from_parent = true); - - // -1 = monospace, 0 means small, font size = 1 means big - void setFontSize(S32 size_index); - - - // Overrides - /*virtual*/ void draw(); -private: - void update(); - - F32 mLinePersistTime; // Age at which to stop drawing. - F32 mFadeTime; // Age at which to start fading - const LLFontGL* mFont; - S32 mConsoleWidth; - S32 mConsoleHeight; - -}; - -extern LLConsole* gConsole; - -#endif +/** + * @file llconsole.h + * @brief a simple console-style output device + * + * $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$ + */ + +#ifndef LL_LLCONSOLE_H +#define LL_LLCONSOLE_H + +#include "llfixedbuffer.h" +#include "lluictrl.h" +#include "v4color.h" +#include + +class LLSD; + +class LLConsole : public LLFixedBuffer, public LLUICtrl, public LLInstanceTracker +{ +public: + + typedef enum e_font_size + { + MONOSPACE = -1, + SMALL = 0, + BIG = 1 + } EFontSize; + + struct Params : public LLInitParam::Block + { + Optional max_lines; + Optional persist_time; + Optional font_size_index; + Params() + : max_lines("max_lines", LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines")), + persist_time("persist_time", 0.f), // forever + font_size_index("font_size_index") + { + changeDefault(mouse_opaque, false); + } + }; +protected: + LLConsole(const Params&); + friend class LLUICtrlFactory; + +public: + // call once per frame to pull data out of LLFixedBuffer + static void updateClass(); + + //A paragraph color segment defines the color of text in a line + //of text that was received for console display. It has no + //notion of line wraps, screen position, or the text it contains. + //It is only the number of characters that are a color and the + //color. + struct ParagraphColorSegment + { + S32 mNumChars; + LLColor4 mColor; + }; + + //A line color segment is a chunk of text, the color associated + //with it, and the X Position it was calculated to begin at + //on the screen. X Positions are re-calculated if the + //screen changes size. + class LineColorSegment + { + public: + LineColorSegment(LLWString text, LLColor4 color, F32 xpos) : mText(text), mColor(color), mXPosition(xpos) {} + public: + LLWString mText; + LLColor4 mColor; + F32 mXPosition; + }; + + typedef std::list line_color_segments_t; + + //A line is composed of one or more color segments. + class Line + { + public: + line_color_segments_t mLineColorSegments; + }; + + typedef std::list lines_t; + typedef std::list paragraph_color_segments_t; + + //A paragraph is a processed element containing the entire text of the + //message (used for recalculating positions on screen resize) + //The time this message was added to the console output + //The visual screen width of the longest line in this block + //And a list of one or more lines which are used to display this message. + class Paragraph + { + public: + Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width); + void makeParagraphColorSegments ( const LLColor4 &color); + void updateLines ( F32 screen_width, const LLFontGL* font, bool force_resize=false ); + public: + LLWString mParagraphText; //The entire text of the paragraph + paragraph_color_segments_t mParagraphColorSegments; + F32 mAddTime; //Time this paragraph was added to the display. + F32 mMaxWidth; //Width of the widest line of text in this paragraph. + lines_t mLines; + + }; + + //The console contains a deque of paragraphs which represent the individual messages. + typedef std::deque paragraph_t; + paragraph_t mParagraphs; + + ~LLConsole(){}; + + // each line lasts this long after being added + void setLinePersistTime(F32 seconds); + + void reshape(S32 width, S32 height, bool called_from_parent = true); + + // -1 = monospace, 0 means small, font size = 1 means big + void setFontSize(S32 size_index); + + + // Overrides + /*virtual*/ void draw(); +private: + void update(); + + F32 mLinePersistTime; // Age at which to stop drawing. + F32 mFadeTime; // Age at which to start fading + const LLFontGL* mFont; + S32 mConsoleWidth; + S32 mConsoleHeight; + +}; + +extern LLConsole* gConsole; + +#endif diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp index a47218c4e2..4c2912cde6 100644 --- a/indra/llui/llcontainerview.cpp +++ b/indra/llui/llcontainerview.cpp @@ -1,300 +1,300 @@ -/** - * @file llcontainerview.cpp - * @brief Container for all statistics info - * - * $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 "linden_common.h" - -#include "llcontainerview.h" - -#include "llerror.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llui.h" -#include "llstring.h" -#include "llscrollcontainer.h" -#include "lluictrlfactory.h" - -static LLDefaultChildRegistry::Register r1("container_view"); - -#include "llpanel.h" -#include "llstatview.h" -static ContainerViewRegistry::Register r2("stat_view"); -static ContainerViewRegistry::Register r3("panel", &LLPanel::fromXML); - -LLContainerView::LLContainerView(const LLContainerView::Params& p) -: LLView(p), - mShowLabel(p.show_label), - mLabel(p.label), - mDisplayChildren(p.display_children) -{ - mScrollContainer = NULL; -} - -LLContainerView::~LLContainerView() -{ - // Children all cleaned up by default view destructor. -} - -bool LLContainerView::postBuild() -{ - setDisplayChildren(mDisplayChildren); - reshape(getRect().getWidth(), getRect().getHeight(), false); - return true; -} - -bool LLContainerView::addChild(LLView* child, S32 tab_group) -{ - bool res = LLView::addChild(child, tab_group); - if (res) - { - sendChildToBack(child); - } - return res; -} - -bool LLContainerView::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - return handleMouseDown(x, y, mask); -} - -bool LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = false; - if (mDisplayChildren) - { - handled = (LLView::childrenHandleMouseDown(x, y, mask) != NULL); - } - if (!handled) - { - if( mShowLabel && (y >= getRect().getHeight() - 10) ) - { - setDisplayChildren(!mDisplayChildren); - reshape(getRect().getWidth(), getRect().getHeight(), false); - handled = true; - } - } - return handled; -} - -bool LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - if (mDisplayChildren) - { - handled = (LLView::childrenHandleMouseUp(x, y, mask) != NULL); - } - return handled; -} - - -void LLContainerView::draw() -{ - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); - } - - // Draw the label - if (mShowLabel) - { - LLFontGL::getFontMonospace()->renderUTF8( - mLabel, 0, 2, getRect().getHeight() - 2, LLColor4(1,1,1,1), LLFontGL::LEFT, LLFontGL::TOP); - } - - LLView::draw(); -} - - -void LLContainerView::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLRect scroller_rect; - scroller_rect.setOriginAndSize(0, 0, width, height); - - if (mScrollContainer) - { - scroller_rect = mScrollContainer->getContentWindowRect(); - } - else - { - // if we're uncontained - make height as small as possible - scroller_rect.mTop = 0; - } - - arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent); - - // sometimes, after layout, our container will change size (scrollbars popping in and out) - // if so, attempt another layout - if (mScrollContainer) - { - LLRect new_container_rect = mScrollContainer->getContentWindowRect(); - - if ((new_container_rect.getWidth() != scroller_rect.getWidth()) || - (new_container_rect.getHeight() != scroller_rect.getHeight())) // the container size has changed, attempt to arrange again - { - arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent); - } - } -} - -void LLContainerView::arrange(S32 width, S32 height, bool called_from_parent) -{ - // Determine the sizes and locations of all contained views - S32 total_height = 0; - S32 top, left, right, bottom; - //LLView *childp; - - // These will be used for the children - left = 10; - top = getRect().getHeight() - 4; - right = width - 2; - bottom = top; - - // Leave some space for the top label/grab handle - if (mShowLabel) - { - total_height += 20; - } - - if (mDisplayChildren) - { - // Determine total height - U32 child_height = 0; - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) - { - LLView *childp = *child_iter; - if (!childp->getVisible()) - { - LL_WARNS() << "Incorrect visibility!" << LL_ENDL; - } - LLRect child_rect = childp->getRequiredRect(); - child_height += child_rect.getHeight(); - child_height += 2; - } - total_height += child_height; - } - - if (total_height < height) - total_height = height; - - LLRect my_rect = getRect(); - if (followsTop()) - { - my_rect.mBottom = my_rect.mTop - total_height; - } - else - { - my_rect.mTop = my_rect.mBottom + total_height; - } - - my_rect.mRight = my_rect.mLeft + width; - setRect(my_rect); - - top = total_height; - if (mShowLabel) - { - top -= 20; - } - - bottom = top; - - if (mDisplayChildren) - { - // Iterate through all children, and put in container from top down. - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) - { - LLView *childp = *child_iter; - LLRect child_rect = childp->getRequiredRect(); - bottom -= child_rect.getHeight(); - LLRect r(left, bottom + child_rect.getHeight(), right, bottom); - childp->setRect(r); - childp->reshape(right - left, top - bottom); - top = bottom - 2; - bottom = top; - } - } - - if (!called_from_parent) - { - if (getParent()) - { - getParent()->reshape(getParent()->getRect().getWidth(), getParent()->getRect().getHeight(), false); - } - } - -} - -LLRect LLContainerView::getRequiredRect() -{ - LLRect req_rect; - //LLView *childp; - U32 total_height = 0; - - // Determine the sizes and locations of all contained views - - // Leave some space for the top label/grab handle - - if (mShowLabel) - { - total_height = 20; - } - - - if (mDisplayChildren) - { - // Determine total height - U32 child_height = 0; - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) - { - LLView *childp = *child_iter; - LLRect child_rect = childp->getRequiredRect(); - child_height += child_rect.getHeight(); - child_height += 2; - } - - total_height += child_height; - } - req_rect.mTop = total_height; - return req_rect; -} - -void LLContainerView::setLabel(const std::string& label) -{ - mLabel = label; -} - -void LLContainerView::setDisplayChildren(bool displayChildren) -{ - mDisplayChildren = displayChildren; - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) - { - LLView *childp = *child_iter; - childp->setVisible(mDisplayChildren); - } -} +/** + * @file llcontainerview.cpp + * @brief Container for all statistics info + * + * $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 "linden_common.h" + +#include "llcontainerview.h" + +#include "llerror.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llui.h" +#include "llstring.h" +#include "llscrollcontainer.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register r1("container_view"); + +#include "llpanel.h" +#include "llstatview.h" +static ContainerViewRegistry::Register r2("stat_view"); +static ContainerViewRegistry::Register r3("panel", &LLPanel::fromXML); + +LLContainerView::LLContainerView(const LLContainerView::Params& p) +: LLView(p), + mShowLabel(p.show_label), + mLabel(p.label), + mDisplayChildren(p.display_children) +{ + mScrollContainer = NULL; +} + +LLContainerView::~LLContainerView() +{ + // Children all cleaned up by default view destructor. +} + +bool LLContainerView::postBuild() +{ + setDisplayChildren(mDisplayChildren); + reshape(getRect().getWidth(), getRect().getHeight(), false); + return true; +} + +bool LLContainerView::addChild(LLView* child, S32 tab_group) +{ + bool res = LLView::addChild(child, tab_group); + if (res) + { + sendChildToBack(child); + } + return res; +} + +bool LLContainerView::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + return handleMouseDown(x, y, mask); +} + +bool LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = false; + if (mDisplayChildren) + { + handled = (LLView::childrenHandleMouseDown(x, y, mask) != NULL); + } + if (!handled) + { + if( mShowLabel && (y >= getRect().getHeight() - 10) ) + { + setDisplayChildren(!mDisplayChildren); + reshape(getRect().getWidth(), getRect().getHeight(), false); + handled = true; + } + } + return handled; +} + +bool LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + if (mDisplayChildren) + { + handled = (LLView::childrenHandleMouseUp(x, y, mask) != NULL); + } + return handled; +} + + +void LLContainerView::draw() +{ + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); + } + + // Draw the label + if (mShowLabel) + { + LLFontGL::getFontMonospace()->renderUTF8( + mLabel, 0, 2, getRect().getHeight() - 2, LLColor4(1,1,1,1), LLFontGL::LEFT, LLFontGL::TOP); + } + + LLView::draw(); +} + + +void LLContainerView::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLRect scroller_rect; + scroller_rect.setOriginAndSize(0, 0, width, height); + + if (mScrollContainer) + { + scroller_rect = mScrollContainer->getContentWindowRect(); + } + else + { + // if we're uncontained - make height as small as possible + scroller_rect.mTop = 0; + } + + arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent); + + // sometimes, after layout, our container will change size (scrollbars popping in and out) + // if so, attempt another layout + if (mScrollContainer) + { + LLRect new_container_rect = mScrollContainer->getContentWindowRect(); + + if ((new_container_rect.getWidth() != scroller_rect.getWidth()) || + (new_container_rect.getHeight() != scroller_rect.getHeight())) // the container size has changed, attempt to arrange again + { + arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent); + } + } +} + +void LLContainerView::arrange(S32 width, S32 height, bool called_from_parent) +{ + // Determine the sizes and locations of all contained views + S32 total_height = 0; + S32 top, left, right, bottom; + //LLView *childp; + + // These will be used for the children + left = 10; + top = getRect().getHeight() - 4; + right = width - 2; + bottom = top; + + // Leave some space for the top label/grab handle + if (mShowLabel) + { + total_height += 20; + } + + if (mDisplayChildren) + { + // Determine total height + U32 child_height = 0; + for (child_list_const_iter_t child_iter = getChildList()->begin(); + child_iter != getChildList()->end(); ++child_iter) + { + LLView *childp = *child_iter; + if (!childp->getVisible()) + { + LL_WARNS() << "Incorrect visibility!" << LL_ENDL; + } + LLRect child_rect = childp->getRequiredRect(); + child_height += child_rect.getHeight(); + child_height += 2; + } + total_height += child_height; + } + + if (total_height < height) + total_height = height; + + LLRect my_rect = getRect(); + if (followsTop()) + { + my_rect.mBottom = my_rect.mTop - total_height; + } + else + { + my_rect.mTop = my_rect.mBottom + total_height; + } + + my_rect.mRight = my_rect.mLeft + width; + setRect(my_rect); + + top = total_height; + if (mShowLabel) + { + top -= 20; + } + + bottom = top; + + if (mDisplayChildren) + { + // Iterate through all children, and put in container from top down. + for (child_list_const_iter_t child_iter = getChildList()->begin(); + child_iter != getChildList()->end(); ++child_iter) + { + LLView *childp = *child_iter; + LLRect child_rect = childp->getRequiredRect(); + bottom -= child_rect.getHeight(); + LLRect r(left, bottom + child_rect.getHeight(), right, bottom); + childp->setRect(r); + childp->reshape(right - left, top - bottom); + top = bottom - 2; + bottom = top; + } + } + + if (!called_from_parent) + { + if (getParent()) + { + getParent()->reshape(getParent()->getRect().getWidth(), getParent()->getRect().getHeight(), false); + } + } + +} + +LLRect LLContainerView::getRequiredRect() +{ + LLRect req_rect; + //LLView *childp; + U32 total_height = 0; + + // Determine the sizes and locations of all contained views + + // Leave some space for the top label/grab handle + + if (mShowLabel) + { + total_height = 20; + } + + + if (mDisplayChildren) + { + // Determine total height + U32 child_height = 0; + for (child_list_const_iter_t child_iter = getChildList()->begin(); + child_iter != getChildList()->end(); ++child_iter) + { + LLView *childp = *child_iter; + LLRect child_rect = childp->getRequiredRect(); + child_height += child_rect.getHeight(); + child_height += 2; + } + + total_height += child_height; + } + req_rect.mTop = total_height; + return req_rect; +} + +void LLContainerView::setLabel(const std::string& label) +{ + mLabel = label; +} + +void LLContainerView::setDisplayChildren(bool displayChildren) +{ + mDisplayChildren = displayChildren; + for (child_list_const_iter_t child_iter = getChildList()->begin(); + child_iter != getChildList()->end(); ++child_iter) + { + LLView *childp = *child_iter; + childp->setVisible(mDisplayChildren); + } +} diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h index 82239eda4e..319fb7d5e9 100644 --- a/indra/llui/llcontainerview.h +++ b/indra/llui/llcontainerview.h @@ -1,94 +1,94 @@ -/** - * @file llcontainerview.h - * @brief Container for all statistics info. - * - * $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$ - */ - -#ifndef LL_LLCONTAINERVIEW_H -#define LL_LLCONTAINERVIEW_H - -#include "stdtypes.h" -#include "lltextbox.h" -#include "llstatbar.h" -#include "llview.h" - -class LLScrollContainer; - -struct ContainerViewRegistry : public LLChildRegistry -{ - LLSINGLETON_EMPTY_CTOR(ContainerViewRegistry); -}; - -class LLContainerView : public LLView -{ -public: - struct Params : public LLInitParam::Block - { - Optional label; - Optional show_label; - Optional display_children; - Params() - : label("label"), - show_label("show_label", false), - display_children("display_children", true) - { - changeDefault(mouse_opaque, false); - } - }; - - // my valid children are stored in this registry - typedef ContainerViewRegistry child_registry_t; - -protected: - LLContainerView(const Params& p); - friend class LLUICtrlFactory; -public: - ~LLContainerView(); - - /*virtual*/ bool postBuild(); - /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); - - /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); - - /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - /*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options. - - void setLabel(const std::string& label); - void showLabel(bool show) { mShowLabel = show; } - void setDisplayChildren(bool displayChildren); - bool getDisplayChildren() { return mDisplayChildren; } - void setScrollContainer(LLScrollContainer* scroll) {mScrollContainer = scroll;} - - private: - LLScrollContainer* mScrollContainer; - void arrange(S32 width, S32 height, bool called_from_parent = true); - bool mShowLabel; - -protected: - bool mDisplayChildren; - std::string mLabel; -}; -#endif // LL_CONTAINERVIEW_ +/** + * @file llcontainerview.h + * @brief Container for all statistics info. + * + * $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$ + */ + +#ifndef LL_LLCONTAINERVIEW_H +#define LL_LLCONTAINERVIEW_H + +#include "stdtypes.h" +#include "lltextbox.h" +#include "llstatbar.h" +#include "llview.h" + +class LLScrollContainer; + +struct ContainerViewRegistry : public LLChildRegistry +{ + LLSINGLETON_EMPTY_CTOR(ContainerViewRegistry); +}; + +class LLContainerView : public LLView +{ +public: + struct Params : public LLInitParam::Block + { + Optional label; + Optional show_label; + Optional display_children; + Params() + : label("label"), + show_label("show_label", false), + display_children("display_children", true) + { + changeDefault(mouse_opaque, false); + } + }; + + // my valid children are stored in this registry + typedef ContainerViewRegistry child_registry_t; + +protected: + LLContainerView(const Params& p); + friend class LLUICtrlFactory; +public: + ~LLContainerView(); + + /*virtual*/ bool postBuild(); + /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); + + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + + /*virtual*/ void draw(); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + /*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options. + + void setLabel(const std::string& label); + void showLabel(bool show) { mShowLabel = show; } + void setDisplayChildren(bool displayChildren); + bool getDisplayChildren() { return mDisplayChildren; } + void setScrollContainer(LLScrollContainer* scroll) {mScrollContainer = scroll;} + + private: + LLScrollContainer* mScrollContainer; + void arrange(S32 width, S32 height, bool called_from_parent = true); + bool mShowLabel; + +protected: + bool mDisplayChildren; + std::string mLabel; +}; +#endif // LL_CONTAINERVIEW_ diff --git a/indra/llui/llctrlselectioninterface.cpp b/indra/llui/llctrlselectioninterface.cpp index 55c901b54c..0a5512afa1 100644 --- a/indra/llui/llctrlselectioninterface.cpp +++ b/indra/llui/llctrlselectioninterface.cpp @@ -1,63 +1,63 @@ -/** - * @file llctrlselectioninterface.cpp - * @brief Programmatic selection of items in a list. - * - * $LicenseInfo:firstyear=2006&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 "llctrlselectioninterface.h" - -#include "llsd.h" - -// virtual -LLCtrlSelectionInterface::~LLCtrlSelectionInterface() -{ } - -bool LLCtrlSelectionInterface::selectByValue(LLSD value) -{ - return setSelectedByValue(value, true); -} - -bool LLCtrlSelectionInterface::deselectByValue(LLSD value) -{ - return setSelectedByValue(value, false); -} - - -// virtual -LLCtrlListInterface::~LLCtrlListInterface() -{ } - -LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value) -{ - return addSimpleElement(value, ADD_BOTTOM, LLSD()); -} - -LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value, EAddPosition pos) -{ - return addSimpleElement(value, pos, LLSD()); -} - -// virtual -LLCtrlScrollInterface::~LLCtrlScrollInterface() -{ } +/** + * @file llctrlselectioninterface.cpp + * @brief Programmatic selection of items in a list. + * + * $LicenseInfo:firstyear=2006&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 "llctrlselectioninterface.h" + +#include "llsd.h" + +// virtual +LLCtrlSelectionInterface::~LLCtrlSelectionInterface() +{ } + +bool LLCtrlSelectionInterface::selectByValue(LLSD value) +{ + return setSelectedByValue(value, true); +} + +bool LLCtrlSelectionInterface::deselectByValue(LLSD value) +{ + return setSelectedByValue(value, false); +} + + +// virtual +LLCtrlListInterface::~LLCtrlListInterface() +{ } + +LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value) +{ + return addSimpleElement(value, ADD_BOTTOM, LLSD()); +} + +LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value, EAddPosition pos) +{ + return addSimpleElement(value, pos, LLSD()); +} + +// virtual +LLCtrlScrollInterface::~LLCtrlScrollInterface() +{ } diff --git a/indra/llui/llctrlselectioninterface.h b/indra/llui/llctrlselectioninterface.h index 01ab9db670..c845f7027d 100644 --- a/indra/llui/llctrlselectioninterface.h +++ b/indra/llui/llctrlselectioninterface.h @@ -1,104 +1,104 @@ -/** - * @file llctrlselectioninterface.h - * @brief Programmatic selection of items in a list. - * - * $LicenseInfo:firstyear=2006&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 LLCTRLSELECTIONINTERFACE_H -#define LLCTRLSELECTIONINTERFACE_H - -#include "stdtypes.h" -#include "llstring.h" -#include "llui.h" - -class LLSD; -class LLUUID; -class LLScrollListItem; - -class LLCtrlSelectionInterface -{ -public: - virtual ~LLCtrlSelectionInterface(); - - enum EOperation - { - OP_DELETE = 1, - OP_SELECT, - OP_DESELECT, - }; - - virtual bool getCanSelect() const = 0; - - virtual S32 getItemCount() const = 0; - - virtual bool selectFirstItem() = 0; - virtual bool selectNthItem( S32 index ) = 0; - virtual bool selectItemRange( S32 first, S32 last ) = 0; - - virtual S32 getFirstSelectedIndex() const = 0; - - // TomY TODO: Simply cast the UUIDs to LLSDs, using the selectByValue function - virtual bool setCurrentByID( const LLUUID& id ) = 0; - virtual LLUUID getCurrentID() const = 0; - - bool selectByValue(const LLSD value); - bool deselectByValue(const LLSD value); - virtual bool setSelectedByValue(const LLSD& value, bool selected) = 0; - virtual LLSD getSelectedValue() = 0; - - virtual bool isSelected(const LLSD& value) const = 0; - - virtual bool operateOnSelection(EOperation op) = 0; - virtual bool operateOnAll(EOperation op) = 0; -}; - -class LLCtrlListInterface : public LLCtrlSelectionInterface -{ -public: - virtual ~LLCtrlListInterface(); - - virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM) = 0; - virtual void clearColumns() = 0; - virtual void setColumnLabel(const std::string& column, const std::string& label) = 0; - // TomY TODO: Document this - virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL) = 0; - - LLScrollListItem* addSimpleElement(const std::string& value); // defaults to bottom - LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos); // defaults to no LLSD() id - virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) = 0; - - virtual void clearRows() = 0; - virtual void sortByColumn(const std::string& name, bool ascending) = 0; -}; - -class LLCtrlScrollInterface -{ -public: - virtual ~LLCtrlScrollInterface(); - - virtual S32 getScrollPos() const = 0; - virtual void setScrollPos( S32 pos ) = 0; - virtual void scrollToShowSelected() = 0; -}; - -#endif +/** + * @file llctrlselectioninterface.h + * @brief Programmatic selection of items in a list. + * + * $LicenseInfo:firstyear=2006&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 LLCTRLSELECTIONINTERFACE_H +#define LLCTRLSELECTIONINTERFACE_H + +#include "stdtypes.h" +#include "llstring.h" +#include "llui.h" + +class LLSD; +class LLUUID; +class LLScrollListItem; + +class LLCtrlSelectionInterface +{ +public: + virtual ~LLCtrlSelectionInterface(); + + enum EOperation + { + OP_DELETE = 1, + OP_SELECT, + OP_DESELECT, + }; + + virtual bool getCanSelect() const = 0; + + virtual S32 getItemCount() const = 0; + + virtual bool selectFirstItem() = 0; + virtual bool selectNthItem( S32 index ) = 0; + virtual bool selectItemRange( S32 first, S32 last ) = 0; + + virtual S32 getFirstSelectedIndex() const = 0; + + // TomY TODO: Simply cast the UUIDs to LLSDs, using the selectByValue function + virtual bool setCurrentByID( const LLUUID& id ) = 0; + virtual LLUUID getCurrentID() const = 0; + + bool selectByValue(const LLSD value); + bool deselectByValue(const LLSD value); + virtual bool setSelectedByValue(const LLSD& value, bool selected) = 0; + virtual LLSD getSelectedValue() = 0; + + virtual bool isSelected(const LLSD& value) const = 0; + + virtual bool operateOnSelection(EOperation op) = 0; + virtual bool operateOnAll(EOperation op) = 0; +}; + +class LLCtrlListInterface : public LLCtrlSelectionInterface +{ +public: + virtual ~LLCtrlListInterface(); + + virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM) = 0; + virtual void clearColumns() = 0; + virtual void setColumnLabel(const std::string& column, const std::string& label) = 0; + // TomY TODO: Document this + virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL) = 0; + + LLScrollListItem* addSimpleElement(const std::string& value); // defaults to bottom + LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos); // defaults to no LLSD() id + virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) = 0; + + virtual void clearRows() = 0; + virtual void sortByColumn(const std::string& name, bool ascending) = 0; +}; + +class LLCtrlScrollInterface +{ +public: + virtual ~LLCtrlScrollInterface(); + + virtual S32 getScrollPos() const = 0; + virtual void setScrollPos( S32 pos ) = 0; + virtual void scrollToShowSelected() = 0; +}; + +#endif diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp index ab61b4617a..19ae03cdf9 100644 --- a/indra/llui/lldockablefloater.cpp +++ b/indra/llui/lldockablefloater.cpp @@ -1,263 +1,263 @@ -/** - * @file lldockablefloater.cpp - * @brief Creates a panel of a specific kind for a toast - * - * $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$ - */ - -#include "linden_common.h" - -#include "lldockablefloater.h" -#include "llfloaterreg.h" - -//static -LLHandle LLDockableFloater::sInstanceHandle; - -//static -void LLDockableFloater::init(LLDockableFloater* thiz) -{ - thiz->setDocked(thiz->mDockControl.get() != NULL - && thiz->mDockControl.get()->isDockVisible()); - thiz->resetInstance(); - - // all dockable floaters should have close, dock and minimize buttons - thiz->setCanClose(true); - thiz->setCanDock(true); - thiz->setCanMinimize(true); - thiz->setOverlapsScreenChannel(false); - thiz->mForceDocking = false; -} - -LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, - const LLSD& key, const Params& params) : - LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(true) -{ - init(this); - mUseTongue = true; -} - -LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, - const LLSD& key, const Params& params) : - LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(uniqueDocking) -{ - init(this); - mUseTongue = true; -} - -LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, - bool useTongue, const LLSD& key, const Params& params) : - LLFloater(key, params), mDockControl(dockControl), mUseTongue(useTongue), mUniqueDocking(uniqueDocking) -{ - init(this); -} - -LLDockableFloater::~LLDockableFloater() -{ -} - -bool LLDockableFloater::postBuild() -{ - // Remember we should force docking when the floater is opened for the first time - if (mIsDockedStateForcedCallback != NULL && mIsDockedStateForcedCallback()) - { - mForceDocking = true; - } - - mDockTongue = LLUI::getUIImage("Flyout_Pointer"); - LLFloater::setDocked(true); - return LLView::postBuild(); -} - -//static -void LLDockableFloater::toggleInstance(const LLSD& sdname) -{ - LLSD key; - std::string name = sdname.asString(); - - LLDockableFloater* instance = - dynamic_cast (LLFloaterReg::findInstance(name)); - // if floater closed or docked - if (instance == NULL || (instance && instance->isDocked())) - { - LLFloaterReg::toggleInstance(name, key); - // restore button toggle state - if (instance != NULL) - { - instance->storeVisibilityControl(); - } - } - // if floater undocked - else if (instance != NULL) - { - instance->setMinimized(false); - if (instance->getVisible()) - { - instance->setVisible(false); - } - else - { - instance->setVisible(true); - gFloaterView->bringToFront(instance); - } - } -} - -void LLDockableFloater::resetInstance() -{ - if (mUniqueDocking && sInstanceHandle.get() != this) - { - if (sInstanceHandle.get() != NULL && sInstanceHandle.get()->isDocked()) - { - sInstanceHandle.get()->setVisible(false); - } - sInstanceHandle = getHandle(); - } -} - -void LLDockableFloater::setVisible(bool visible) -{ - // Force docking if requested - if (visible && mForceDocking) - { - setCanDock(true); - setDocked(true); - mForceDocking = false; - } - - if(visible && isDocked()) - { - resetInstance(); - } - - if (visible && mDockControl.get() != NULL) - { - mDockControl.get()->repositionDockable(); - } - - if (visible && !isMinimized()) - { - LLFloater::setFrontmost(getAutoFocus()); - } - LLFloater::setVisible(visible); -} - -void LLDockableFloater::setMinimized(bool minimize) -{ - if(minimize && isDocked()) - { - // minimizing a docked floater just hides it - setVisible(false); - } - else - { - LLFloater::setMinimized(minimize); - } -} - -LLView * LLDockableFloater::getDockWidget() -{ - LLView * res = NULL; - if (getDockControl() != NULL) { - res = getDockControl()->getDock(); - } - - return res; -} - -void LLDockableFloater::onDockHidden() -{ - setCanDock(false); -} - -void LLDockableFloater::onDockShown() -{ - if (!isMinimized()) - { - setCanDock(true); - } -} - -void LLDockableFloater::setDocked(bool docked, bool pop_on_undock) -{ - if (mDockControl.get() != NULL && mDockControl.get()->isDockVisible()) - { - if (docked) - { - resetInstance(); - mDockControl.get()->on(); - } - else - { - mDockControl.get()->off(); - } - - if (!docked && pop_on_undock) - { - // visually pop up a little bit to emphasize the undocking - translate(0, UNDOCK_LEAP_HEIGHT); - } - } - - LLFloater::setDocked(docked, pop_on_undock); -} - -void LLDockableFloater::draw() -{ - if (mDockControl.get() != NULL) - { - mDockControl.get()->repositionDockable(); - if (isDocked()) - { - mDockControl.get()->drawToungue(); - } - } - LLFloater::draw(); -} - -void LLDockableFloater::setDockControl(LLDockControl* dockControl) -{ - mDockControl.reset(dockControl); - setDocked(isDocked()); -} - -const LLUIImagePtr& LLDockableFloater::getDockTongue(LLDockControl::DocAt dock_side) -{ - switch(dock_side) - { - case LLDockControl::LEFT: - mDockTongue = LLUI::getUIImage("Flyout_Left"); - break; - case LLDockControl::RIGHT: - mDockTongue = LLUI::getUIImage("Flyout_Right"); - break; - default: - mDockTongue = LLUI::getUIImage("Flyout_Pointer"); - break; - } - - return mDockTongue; -} - -LLDockControl* LLDockableFloater::getDockControl() -{ - return mDockControl.get(); -} +/** + * @file lldockablefloater.cpp + * @brief Creates a panel of a specific kind for a toast + * + * $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$ + */ + +#include "linden_common.h" + +#include "lldockablefloater.h" +#include "llfloaterreg.h" + +//static +LLHandle LLDockableFloater::sInstanceHandle; + +//static +void LLDockableFloater::init(LLDockableFloater* thiz) +{ + thiz->setDocked(thiz->mDockControl.get() != NULL + && thiz->mDockControl.get()->isDockVisible()); + thiz->resetInstance(); + + // all dockable floaters should have close, dock and minimize buttons + thiz->setCanClose(true); + thiz->setCanDock(true); + thiz->setCanMinimize(true); + thiz->setOverlapsScreenChannel(false); + thiz->mForceDocking = false; +} + +LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, + const LLSD& key, const Params& params) : + LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(true) +{ + init(this); + mUseTongue = true; +} + +LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + const LLSD& key, const Params& params) : + LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(uniqueDocking) +{ + init(this); + mUseTongue = true; +} + +LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + bool useTongue, const LLSD& key, const Params& params) : + LLFloater(key, params), mDockControl(dockControl), mUseTongue(useTongue), mUniqueDocking(uniqueDocking) +{ + init(this); +} + +LLDockableFloater::~LLDockableFloater() +{ +} + +bool LLDockableFloater::postBuild() +{ + // Remember we should force docking when the floater is opened for the first time + if (mIsDockedStateForcedCallback != NULL && mIsDockedStateForcedCallback()) + { + mForceDocking = true; + } + + mDockTongue = LLUI::getUIImage("Flyout_Pointer"); + LLFloater::setDocked(true); + return LLView::postBuild(); +} + +//static +void LLDockableFloater::toggleInstance(const LLSD& sdname) +{ + LLSD key; + std::string name = sdname.asString(); + + LLDockableFloater* instance = + dynamic_cast (LLFloaterReg::findInstance(name)); + // if floater closed or docked + if (instance == NULL || (instance && instance->isDocked())) + { + LLFloaterReg::toggleInstance(name, key); + // restore button toggle state + if (instance != NULL) + { + instance->storeVisibilityControl(); + } + } + // if floater undocked + else if (instance != NULL) + { + instance->setMinimized(false); + if (instance->getVisible()) + { + instance->setVisible(false); + } + else + { + instance->setVisible(true); + gFloaterView->bringToFront(instance); + } + } +} + +void LLDockableFloater::resetInstance() +{ + if (mUniqueDocking && sInstanceHandle.get() != this) + { + if (sInstanceHandle.get() != NULL && sInstanceHandle.get()->isDocked()) + { + sInstanceHandle.get()->setVisible(false); + } + sInstanceHandle = getHandle(); + } +} + +void LLDockableFloater::setVisible(bool visible) +{ + // Force docking if requested + if (visible && mForceDocking) + { + setCanDock(true); + setDocked(true); + mForceDocking = false; + } + + if(visible && isDocked()) + { + resetInstance(); + } + + if (visible && mDockControl.get() != NULL) + { + mDockControl.get()->repositionDockable(); + } + + if (visible && !isMinimized()) + { + LLFloater::setFrontmost(getAutoFocus()); + } + LLFloater::setVisible(visible); +} + +void LLDockableFloater::setMinimized(bool minimize) +{ + if(minimize && isDocked()) + { + // minimizing a docked floater just hides it + setVisible(false); + } + else + { + LLFloater::setMinimized(minimize); + } +} + +LLView * LLDockableFloater::getDockWidget() +{ + LLView * res = NULL; + if (getDockControl() != NULL) { + res = getDockControl()->getDock(); + } + + return res; +} + +void LLDockableFloater::onDockHidden() +{ + setCanDock(false); +} + +void LLDockableFloater::onDockShown() +{ + if (!isMinimized()) + { + setCanDock(true); + } +} + +void LLDockableFloater::setDocked(bool docked, bool pop_on_undock) +{ + if (mDockControl.get() != NULL && mDockControl.get()->isDockVisible()) + { + if (docked) + { + resetInstance(); + mDockControl.get()->on(); + } + else + { + mDockControl.get()->off(); + } + + if (!docked && pop_on_undock) + { + // visually pop up a little bit to emphasize the undocking + translate(0, UNDOCK_LEAP_HEIGHT); + } + } + + LLFloater::setDocked(docked, pop_on_undock); +} + +void LLDockableFloater::draw() +{ + if (mDockControl.get() != NULL) + { + mDockControl.get()->repositionDockable(); + if (isDocked()) + { + mDockControl.get()->drawToungue(); + } + } + LLFloater::draw(); +} + +void LLDockableFloater::setDockControl(LLDockControl* dockControl) +{ + mDockControl.reset(dockControl); + setDocked(isDocked()); +} + +const LLUIImagePtr& LLDockableFloater::getDockTongue(LLDockControl::DocAt dock_side) +{ + switch(dock_side) + { + case LLDockControl::LEFT: + mDockTongue = LLUI::getUIImage("Flyout_Left"); + break; + case LLDockControl::RIGHT: + mDockTongue = LLUI::getUIImage("Flyout_Right"); + break; + default: + mDockTongue = LLUI::getUIImage("Flyout_Pointer"); + break; + } + + return mDockTongue; +} + +LLDockControl* LLDockableFloater::getDockControl() +{ + return mDockControl.get(); +} diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h index fd23877e09..3effc977db 100644 --- a/indra/llui/lldockablefloater.h +++ b/indra/llui/lldockablefloater.h @@ -1,152 +1,152 @@ -/** - * @file lldockablefloater.h - * @brief Creates a panel of a specific kind for a toast. - * - * $LicenseInfo:firstyear=2003&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_DOCKABLEFLOATER_H -#define LL_DOCKABLEFLOATER_H - -#include "llerror.h" -#include "llfloater.h" -#include "lldockcontrol.h" -#include - -/** - * Represents floater that can dock. - * In case impossibility deriving from LLDockableFloater use LLDockControl. - */ -class LLDockableFloater : public LLFloater -{ - static const U32 UNDOCK_LEAP_HEIGHT = 12; - - static void init(LLDockableFloater* thiz); -public: - LOG_CLASS(LLDockableFloater); - LLDockableFloater(LLDockControl* dockControl, const LLSD& key, - const Params& params = getDefaultParams()); - - /** - * Constructor. - * @param dockControl a pointer to the doc control instance - * @param uniqueDocking - a flag defines is docking should work as tab(at one - * moment only one docked floater can be shown), also this flag defines is dock - * tongue should be used. - * @params key a floater key. - * @params params a floater parameters - */ - LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, - const LLSD& key, const Params& params = getDefaultParams()); - - /** - * Constructor. - * @param dockControl a pointer to the doc control instance - * @param uniqueDocking - a flag defines is docking should work as tab(at one - * moment only one docked floater can be shown). - * @praram useTongue - a flag defines is dock tongue should be used. - * @params key a floater key. - * @params params a floater parameters - */ - LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, - bool useTongue, const LLSD& key, - const Params& params = getDefaultParams()); - - virtual ~LLDockableFloater(); - - static LLHandle getInstanceHandle() { return sInstanceHandle; } - - static void toggleInstance(const LLSD& sdname); - - /** - * If descendant class overrides postBuild() in order to perform specific - * construction then it must still invoke its superclass' implementation. - */ - /* virtula */bool postBuild(); - /* virtual */void setDocked(bool docked, bool pop_on_undock = true); - /* virtual */void draw(); - - /** - * If descendant class overrides setVisible() then it must still invoke its - * superclass' implementation. - */ - /*virtual*/ void setVisible(bool visible); - - /** - * If descendant class overrides setMinimized() then it must still invoke its - * superclass' implementation. - */ - /*virtual*/ void setMinimized(bool minimize); - - LLView * getDockWidget(); - - virtual void onDockHidden(); - virtual void onDockShown(); - - LLDockControl* getDockControl(); - - /** - * Returns true if screen channel should consider floater's size when drawing toasts. - * - * By default returns false. - */ - virtual bool overlapsScreenChannel() { return mOverlapsScreenChannel && getVisible() && isDocked(); } - virtual void setOverlapsScreenChannel(bool overlaps) { mOverlapsScreenChannel = overlaps; } - - bool getUniqueDocking() { return mUniqueDocking; } - bool getUseTongue() { return mUseTongue; } - - void setUseTongue(bool use_tongue) { mUseTongue = use_tongue;} -private: - /** - * Provides unique of dockable floater. - * If dockable floater already exists it should be closed. - */ - void resetInstance(); - -protected: - void setDockControl(LLDockControl* dockControl); - const LLUIImagePtr& getDockTongue(LLDockControl::DocAt dock_side = LLDockControl::TOP); - - // Checks if docking should be forced. - // It may be useful e.g. if floater created in mouselook mode (see EXT-5609) - boost::function mIsDockedStateForcedCallback; - -private: - std::unique_ptr mDockControl; - LLUIImagePtr mDockTongue; - static LLHandle sInstanceHandle; - /** - * Provides possibility to define that dockable floaters can be docked - * non exclusively. - */ - bool mUniqueDocking; - - bool mUseTongue; - - bool mOverlapsScreenChannel; - - // Force docking when the floater is being shown for the first time. - bool mForceDocking; -}; - -#endif /* LL_DOCKABLEFLOATER_H */ +/** + * @file lldockablefloater.h + * @brief Creates a panel of a specific kind for a toast. + * + * $LicenseInfo:firstyear=2003&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_DOCKABLEFLOATER_H +#define LL_DOCKABLEFLOATER_H + +#include "llerror.h" +#include "llfloater.h" +#include "lldockcontrol.h" +#include + +/** + * Represents floater that can dock. + * In case impossibility deriving from LLDockableFloater use LLDockControl. + */ +class LLDockableFloater : public LLFloater +{ + static const U32 UNDOCK_LEAP_HEIGHT = 12; + + static void init(LLDockableFloater* thiz); +public: + LOG_CLASS(LLDockableFloater); + LLDockableFloater(LLDockControl* dockControl, const LLSD& key, + const Params& params = getDefaultParams()); + + /** + * Constructor. + * @param dockControl a pointer to the doc control instance + * @param uniqueDocking - a flag defines is docking should work as tab(at one + * moment only one docked floater can be shown), also this flag defines is dock + * tongue should be used. + * @params key a floater key. + * @params params a floater parameters + */ + LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + const LLSD& key, const Params& params = getDefaultParams()); + + /** + * Constructor. + * @param dockControl a pointer to the doc control instance + * @param uniqueDocking - a flag defines is docking should work as tab(at one + * moment only one docked floater can be shown). + * @praram useTongue - a flag defines is dock tongue should be used. + * @params key a floater key. + * @params params a floater parameters + */ + LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + bool useTongue, const LLSD& key, + const Params& params = getDefaultParams()); + + virtual ~LLDockableFloater(); + + static LLHandle getInstanceHandle() { return sInstanceHandle; } + + static void toggleInstance(const LLSD& sdname); + + /** + * If descendant class overrides postBuild() in order to perform specific + * construction then it must still invoke its superclass' implementation. + */ + /* virtula */bool postBuild(); + /* virtual */void setDocked(bool docked, bool pop_on_undock = true); + /* virtual */void draw(); + + /** + * If descendant class overrides setVisible() then it must still invoke its + * superclass' implementation. + */ + /*virtual*/ void setVisible(bool visible); + + /** + * If descendant class overrides setMinimized() then it must still invoke its + * superclass' implementation. + */ + /*virtual*/ void setMinimized(bool minimize); + + LLView * getDockWidget(); + + virtual void onDockHidden(); + virtual void onDockShown(); + + LLDockControl* getDockControl(); + + /** + * Returns true if screen channel should consider floater's size when drawing toasts. + * + * By default returns false. + */ + virtual bool overlapsScreenChannel() { return mOverlapsScreenChannel && getVisible() && isDocked(); } + virtual void setOverlapsScreenChannel(bool overlaps) { mOverlapsScreenChannel = overlaps; } + + bool getUniqueDocking() { return mUniqueDocking; } + bool getUseTongue() { return mUseTongue; } + + void setUseTongue(bool use_tongue) { mUseTongue = use_tongue;} +private: + /** + * Provides unique of dockable floater. + * If dockable floater already exists it should be closed. + */ + void resetInstance(); + +protected: + void setDockControl(LLDockControl* dockControl); + const LLUIImagePtr& getDockTongue(LLDockControl::DocAt dock_side = LLDockControl::TOP); + + // Checks if docking should be forced. + // It may be useful e.g. if floater created in mouselook mode (see EXT-5609) + boost::function mIsDockedStateForcedCallback; + +private: + std::unique_ptr mDockControl; + LLUIImagePtr mDockTongue; + static LLHandle sInstanceHandle; + /** + * Provides possibility to define that dockable floaters can be docked + * non exclusively. + */ + bool mUniqueDocking; + + bool mUseTongue; + + bool mOverlapsScreenChannel; + + // Force docking when the floater is being shown for the first time. + bool mForceDocking; +}; + +#endif /* LL_DOCKABLEFLOATER_H */ diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index 78efdfc639..15536178ab 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -1,387 +1,387 @@ -/** - * @file lldraghandle.cpp - * @brief LLDragHandle base class - * - * $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$ - */ - -// A widget for dragging a view around the screen using the mouse. - -#include "linden_common.h" - -#include "lldraghandle.h" - -#include "llmath.h" - -//#include "llviewerwindow.h" -#include "llui.h" -#include "llmenugl.h" -#include "lltextbox.h" -#include "llcontrol.h" -#include "llfontgl.h" -#include "llwindow.h" -#include "llfocusmgr.h" -#include "lluictrlfactory.h" - -const S32 LEADING_PAD = 5; -const S32 TITLE_HPAD = 8; -const S32 BORDER_PAD = 1; -const S32 LEFT_PAD = BORDER_PAD + TITLE_HPAD + LEADING_PAD; - -S32 LLDragHandle::sSnapMargin = 5; - -LLDragHandle::LLDragHandle(const LLDragHandle::Params& p) -: LLView(p), - mDragLastScreenX( 0 ), - mDragLastScreenY( 0 ), - mLastMouseScreenX( 0 ), - mLastMouseScreenY( 0 ), - mTitleBox( NULL ), - mMaxTitleWidth( 0 ), - mForeground( true ), - mDragHighlightColor(p.drag_highlight_color()), - mDragShadowColor(p.drag_shadow_color()) - -{ - static LLUICachedControl snap_margin ("SnapMargin", 0); - sSnapMargin = snap_margin; -} - -LLDragHandle::~LLDragHandle() -{ - gFocusMgr.removeKeyboardFocusWithoutCallback(this); - removeChild(mTitleBox); - delete mTitleBox; -} - -void LLDragHandle::initFromParams(const LLDragHandle::Params& p) -{ - LLView::initFromParams(p); - setTitle( p.label ); -} - -void LLDragHandle::setTitleVisible(bool visible) -{ - if(mTitleBox) - { - mTitleBox->setVisible(visible); - } -} - -void LLDragHandleTop::setTitle(const std::string& title) -{ - std::string trimmed_title = title; - LLStringUtil::trim(trimmed_title); - - if( mTitleBox ) - { - mTitleBox->setText(trimmed_title); - } - else - { - const LLFontGL* font = LLFontGL::getFontSansSerif(); - LLTextBox::Params params; - params.name("Drag Handle Title"); - params.rect(getRect()); - params.initial_value(trimmed_title); - params.font(font); - params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT); - params.font_shadow(LLFontGL::DROP_SHADOW_SOFT); - params.use_ellipses = true; - params.parse_urls = false; //cancel URL replacement in floater title - mTitleBox = LLUICtrlFactory::create (params); - addChild( mTitleBox ); - } - - reshapeTitleBox(); -} - - -std::string LLDragHandleTop::getTitle() const -{ - return mTitleBox == NULL ? LLStringUtil::null : mTitleBox->getText(); -} - - -void LLDragHandleLeft::setTitle(const std::string& ) -{ - if( mTitleBox ) - { - removeChild(mTitleBox); - delete mTitleBox; - mTitleBox = NULL; - } - /* no title on left edge */ -} - - -std::string LLDragHandleLeft::getTitle() const -{ - return LLStringUtil::null; -} - - -void LLDragHandleTop::draw() -{ - /* Disable lines. Can drag anywhere in most windows. JC - if( getVisible() && getEnabled() && mForeground) - { - const S32 BORDER_PAD = 2; - const S32 HPAD = 2; - const S32 VPAD = 2; - S32 left = BORDER_PAD + HPAD; - S32 top = getRect().getHeight() - 2 * VPAD; - S32 right = getRect().getWidth() - HPAD; -// S32 bottom = VPAD; - - // draw lines for drag areas - - const S32 LINE_SPACING = (DRAG_HANDLE_HEIGHT - 2 * VPAD) / 4; - S32 line = top - LINE_SPACING; - - LLRect title_rect = mTitleBox->getRect(); - S32 title_right = title_rect.mLeft + mTitleWidth; - bool show_right_side = title_right < getRect().getWidth(); - - for( S32 i=0; i<4; i++ ) - { - gl_line_2d(left, line+1, title_rect.mLeft - LEADING_PAD, line+1, mDragHighlightColor); - if( show_right_side ) - { - gl_line_2d(title_right, line+1, right, line+1, mDragHighlightColor); - } - - gl_line_2d(left, line, title_rect.mLeft - LEADING_PAD, line, mDragShadowColor); - if( show_right_side ) - { - gl_line_2d(title_right, line, right, line, mDragShadowColor); - } - line -= LINE_SPACING; - } - } - */ - - // Colorize the text to match the frontmost state - if (mTitleBox) - { - mTitleBox->setEnabled(getForeground()); - } - - LLView::draw(); -} - - -// assumes GL state is set for 2D -void LLDragHandleLeft::draw() -{ - /* Disable lines. Can drag anywhere in most windows. JC - if( getVisible() && getEnabled() && mForeground ) - { - const S32 BORDER_PAD = 2; -// const S32 HPAD = 2; - const S32 VPAD = 2; - const S32 LINE_SPACING = 3; - - S32 left = BORDER_PAD + LINE_SPACING; - S32 top = getRect().getHeight() - 2 * VPAD; -// S32 right = getRect().getWidth() - HPAD; - S32 bottom = VPAD; - - // draw lines for drag areas - - // no titles yet - //LLRect title_rect = mTitleBox->getRect(); - //S32 title_right = title_rect.mLeft + mTitleWidth; - //bool show_right_side = title_right < getRect().getWidth(); - - S32 line = left; - for( S32 i=0; i<4; i++ ) - { - gl_line_2d(line, top, line, bottom, mDragHighlightColor); - - gl_line_2d(line+1, top, line+1, bottom, mDragShadowColor); - - line += LINE_SPACING; - } - } - */ - - // Colorize the text to match the frontmost state - if (mTitleBox) - { - mTitleBox->setEnabled(getForeground()); - } - - LLView::draw(); -} - -void LLDragHandleTop::reshapeTitleBox() -{ - static LLUICachedControl title_vpad("UIFloaterTitleVPad", 0); - if( ! mTitleBox) - { - return; - } - const LLFontGL* font = LLFontGL::getFontSansSerif(); - S32 title_width = getRect().getWidth(); - title_width -= LEFT_PAD + 2 * BORDER_PAD + getButtonsRect().getWidth(); - S32 title_height = font->getLineHeight(); - LLRect title_rect; - title_rect.setLeftTopAndSize( - LEFT_PAD, - getRect().getHeight() - title_vpad, - title_width, - title_height); - - // calls reshape on mTitleBox - mTitleBox->setShape( title_rect ); -} - -void LLDragHandleTop::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLView::reshape(width, height, called_from_parent); - reshapeTitleBox(); -} - -void LLDragHandleLeft::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLView::reshape(width, height, called_from_parent); -} - -//------------------------------------------------------------- -// UI event handling -//------------------------------------------------------------- - -bool LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // Route future Mouse messages here preemptively. (Release on mouse up.) - // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture(this); - - localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); - mLastMouseScreenX = mDragLastScreenX; - mLastMouseScreenY = mDragLastScreenY; - - // Note: don't pass on to children - return true; -} - - -bool LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if( hasMouseCapture() ) - { - // Release the mouse - gFocusMgr.setMouseCapture( NULL ); - } - - // Note: don't pass on to children - return true; -} - - -bool LLDragHandle::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // We only handle the click if the click both started and ended within us - if( hasMouseCapture() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y); - - // Resize the parent - S32 delta_x = screen_x - mDragLastScreenX; - S32 delta_y = screen_y - mDragLastScreenY; - - // if dragging a docked floater we want to undock - LLFloater * parent = dynamic_cast(getParent()); - if (parent && parent->isDocked()) - { - const S32 SLOP = 12; - - if (delta_y <= -SLOP || - delta_y >= SLOP) - { - parent->setDocked(false, false); - return true; - } - else - { - return false; - } - } - - LLRect original_rect = getParent()->getRect(); - LLRect translated_rect = getParent()->getRect(); - translated_rect.translate(delta_x, delta_y); - // temporarily slam dragged window to new position - getParent()->setRect(translated_rect); - S32 pre_snap_x = getParent()->getRect().mLeft; - S32 pre_snap_y = getParent()->getRect().mBottom; - mDragLastScreenX = screen_x; - mDragLastScreenY = screen_y; - - LLRect new_rect; - LLCoordGL mouse_dir; - // use hysteresis on mouse motion to preserve user intent when mouse stops moving - mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; - mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; - mLastMouseDir = mouse_dir; - mLastMouseScreenX = screen_x; - mLastMouseScreenY = screen_y; - - LLView* snap_view = getParent()->findSnapRect(new_rect, mouse_dir, SNAP_PARENT_AND_SIBLINGS, sSnapMargin); - - getParent()->setSnappedTo(snap_view); - delta_x = new_rect.mLeft - pre_snap_x; - delta_y = new_rect.mBottom - pre_snap_y; - translated_rect.translate(delta_x, delta_y); - - // restore original rect so delta are detected, then call user reshape method to handle snapped floaters, etc - getParent()->setRect(original_rect); - getParent()->setShape(translated_rect, true); - - mDragLastScreenX += delta_x; - mDragLastScreenY += delta_y; - - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" <setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = true; - } - - // Note: don't pass on to children - - return handled; -} - -void LLDragHandle::setValue(const LLSD& value) -{ - setTitle(value.asString()); -} +/** + * @file lldraghandle.cpp + * @brief LLDragHandle base class + * + * $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$ + */ + +// A widget for dragging a view around the screen using the mouse. + +#include "linden_common.h" + +#include "lldraghandle.h" + +#include "llmath.h" + +//#include "llviewerwindow.h" +#include "llui.h" +#include "llmenugl.h" +#include "lltextbox.h" +#include "llcontrol.h" +#include "llfontgl.h" +#include "llwindow.h" +#include "llfocusmgr.h" +#include "lluictrlfactory.h" + +const S32 LEADING_PAD = 5; +const S32 TITLE_HPAD = 8; +const S32 BORDER_PAD = 1; +const S32 LEFT_PAD = BORDER_PAD + TITLE_HPAD + LEADING_PAD; + +S32 LLDragHandle::sSnapMargin = 5; + +LLDragHandle::LLDragHandle(const LLDragHandle::Params& p) +: LLView(p), + mDragLastScreenX( 0 ), + mDragLastScreenY( 0 ), + mLastMouseScreenX( 0 ), + mLastMouseScreenY( 0 ), + mTitleBox( NULL ), + mMaxTitleWidth( 0 ), + mForeground( true ), + mDragHighlightColor(p.drag_highlight_color()), + mDragShadowColor(p.drag_shadow_color()) + +{ + static LLUICachedControl snap_margin ("SnapMargin", 0); + sSnapMargin = snap_margin; +} + +LLDragHandle::~LLDragHandle() +{ + gFocusMgr.removeKeyboardFocusWithoutCallback(this); + removeChild(mTitleBox); + delete mTitleBox; +} + +void LLDragHandle::initFromParams(const LLDragHandle::Params& p) +{ + LLView::initFromParams(p); + setTitle( p.label ); +} + +void LLDragHandle::setTitleVisible(bool visible) +{ + if(mTitleBox) + { + mTitleBox->setVisible(visible); + } +} + +void LLDragHandleTop::setTitle(const std::string& title) +{ + std::string trimmed_title = title; + LLStringUtil::trim(trimmed_title); + + if( mTitleBox ) + { + mTitleBox->setText(trimmed_title); + } + else + { + const LLFontGL* font = LLFontGL::getFontSansSerif(); + LLTextBox::Params params; + params.name("Drag Handle Title"); + params.rect(getRect()); + params.initial_value(trimmed_title); + params.font(font); + params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT); + params.font_shadow(LLFontGL::DROP_SHADOW_SOFT); + params.use_ellipses = true; + params.parse_urls = false; //cancel URL replacement in floater title + mTitleBox = LLUICtrlFactory::create (params); + addChild( mTitleBox ); + } + + reshapeTitleBox(); +} + + +std::string LLDragHandleTop::getTitle() const +{ + return mTitleBox == NULL ? LLStringUtil::null : mTitleBox->getText(); +} + + +void LLDragHandleLeft::setTitle(const std::string& ) +{ + if( mTitleBox ) + { + removeChild(mTitleBox); + delete mTitleBox; + mTitleBox = NULL; + } + /* no title on left edge */ +} + + +std::string LLDragHandleLeft::getTitle() const +{ + return LLStringUtil::null; +} + + +void LLDragHandleTop::draw() +{ + /* Disable lines. Can drag anywhere in most windows. JC + if( getVisible() && getEnabled() && mForeground) + { + const S32 BORDER_PAD = 2; + const S32 HPAD = 2; + const S32 VPAD = 2; + S32 left = BORDER_PAD + HPAD; + S32 top = getRect().getHeight() - 2 * VPAD; + S32 right = getRect().getWidth() - HPAD; +// S32 bottom = VPAD; + + // draw lines for drag areas + + const S32 LINE_SPACING = (DRAG_HANDLE_HEIGHT - 2 * VPAD) / 4; + S32 line = top - LINE_SPACING; + + LLRect title_rect = mTitleBox->getRect(); + S32 title_right = title_rect.mLeft + mTitleWidth; + bool show_right_side = title_right < getRect().getWidth(); + + for( S32 i=0; i<4; i++ ) + { + gl_line_2d(left, line+1, title_rect.mLeft - LEADING_PAD, line+1, mDragHighlightColor); + if( show_right_side ) + { + gl_line_2d(title_right, line+1, right, line+1, mDragHighlightColor); + } + + gl_line_2d(left, line, title_rect.mLeft - LEADING_PAD, line, mDragShadowColor); + if( show_right_side ) + { + gl_line_2d(title_right, line, right, line, mDragShadowColor); + } + line -= LINE_SPACING; + } + } + */ + + // Colorize the text to match the frontmost state + if (mTitleBox) + { + mTitleBox->setEnabled(getForeground()); + } + + LLView::draw(); +} + + +// assumes GL state is set for 2D +void LLDragHandleLeft::draw() +{ + /* Disable lines. Can drag anywhere in most windows. JC + if( getVisible() && getEnabled() && mForeground ) + { + const S32 BORDER_PAD = 2; +// const S32 HPAD = 2; + const S32 VPAD = 2; + const S32 LINE_SPACING = 3; + + S32 left = BORDER_PAD + LINE_SPACING; + S32 top = getRect().getHeight() - 2 * VPAD; +// S32 right = getRect().getWidth() - HPAD; + S32 bottom = VPAD; + + // draw lines for drag areas + + // no titles yet + //LLRect title_rect = mTitleBox->getRect(); + //S32 title_right = title_rect.mLeft + mTitleWidth; + //bool show_right_side = title_right < getRect().getWidth(); + + S32 line = left; + for( S32 i=0; i<4; i++ ) + { + gl_line_2d(line, top, line, bottom, mDragHighlightColor); + + gl_line_2d(line+1, top, line+1, bottom, mDragShadowColor); + + line += LINE_SPACING; + } + } + */ + + // Colorize the text to match the frontmost state + if (mTitleBox) + { + mTitleBox->setEnabled(getForeground()); + } + + LLView::draw(); +} + +void LLDragHandleTop::reshapeTitleBox() +{ + static LLUICachedControl title_vpad("UIFloaterTitleVPad", 0); + if( ! mTitleBox) + { + return; + } + const LLFontGL* font = LLFontGL::getFontSansSerif(); + S32 title_width = getRect().getWidth(); + title_width -= LEFT_PAD + 2 * BORDER_PAD + getButtonsRect().getWidth(); + S32 title_height = font->getLineHeight(); + LLRect title_rect; + title_rect.setLeftTopAndSize( + LEFT_PAD, + getRect().getHeight() - title_vpad, + title_width, + title_height); + + // calls reshape on mTitleBox + mTitleBox->setShape( title_rect ); +} + +void LLDragHandleTop::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLView::reshape(width, height, called_from_parent); + reshapeTitleBox(); +} + +void LLDragHandleLeft::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLView::reshape(width, height, called_from_parent); +} + +//------------------------------------------------------------- +// UI event handling +//------------------------------------------------------------- + +bool LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // Route future Mouse messages here preemptively. (Release on mouse up.) + // No handler needed for focus lost since this clas has no state that depends on it. + gFocusMgr.setMouseCapture(this); + + localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); + mLastMouseScreenX = mDragLastScreenX; + mLastMouseScreenY = mDragLastScreenY; + + // Note: don't pass on to children + return true; +} + + +bool LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if( hasMouseCapture() ) + { + // Release the mouse + gFocusMgr.setMouseCapture( NULL ); + } + + // Note: don't pass on to children + return true; +} + + +bool LLDragHandle::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // We only handle the click if the click both started and ended within us + if( hasMouseCapture() ) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + // Resize the parent + S32 delta_x = screen_x - mDragLastScreenX; + S32 delta_y = screen_y - mDragLastScreenY; + + // if dragging a docked floater we want to undock + LLFloater * parent = dynamic_cast(getParent()); + if (parent && parent->isDocked()) + { + const S32 SLOP = 12; + + if (delta_y <= -SLOP || + delta_y >= SLOP) + { + parent->setDocked(false, false); + return true; + } + else + { + return false; + } + } + + LLRect original_rect = getParent()->getRect(); + LLRect translated_rect = getParent()->getRect(); + translated_rect.translate(delta_x, delta_y); + // temporarily slam dragged window to new position + getParent()->setRect(translated_rect); + S32 pre_snap_x = getParent()->getRect().mLeft; + S32 pre_snap_y = getParent()->getRect().mBottom; + mDragLastScreenX = screen_x; + mDragLastScreenY = screen_y; + + LLRect new_rect; + LLCoordGL mouse_dir; + // use hysteresis on mouse motion to preserve user intent when mouse stops moving + mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; + mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; + mLastMouseDir = mouse_dir; + mLastMouseScreenX = screen_x; + mLastMouseScreenY = screen_y; + + LLView* snap_view = getParent()->findSnapRect(new_rect, mouse_dir, SNAP_PARENT_AND_SIBLINGS, sSnapMargin); + + getParent()->setSnappedTo(snap_view); + delta_x = new_rect.mLeft - pre_snap_x; + delta_y = new_rect.mBottom - pre_snap_y; + translated_rect.translate(delta_x, delta_y); + + // restore original rect so delta are detected, then call user reshape method to handle snapped floaters, etc + getParent()->setRect(original_rect); + getParent()->setShape(translated_rect, true); + + mDragLastScreenX += delta_x; + mDragLastScreenY += delta_y; + + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" <setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; + handled = true; + } + + // Note: don't pass on to children + + return handled; +} + +void LLDragHandle::setValue(const LLSD& value) +{ + setTitle(value.asString()); +} diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h index e7e9469acb..a522e63243 100644 --- a/indra/llui/lldraghandle.h +++ b/indra/llui/lldraghandle.h @@ -1,139 +1,139 @@ -/** - * @file lldraghandle.h - * @brief LLDragHandle base class - * - * $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$ - */ - -// A widget for dragging a view around the screen using the mouse. - -#ifndef LL_DRAGHANDLE_H -#define LL_DRAGHANDLE_H - -#include "llview.h" -#include "v4color.h" -#include "llrect.h" -#include "llcoord.h" - -class LLTextBox; - -class LLDragHandle : public LLView -{ -public: - struct Params - : public LLInitParam::Block - { - Optional label; - Optional drag_highlight_color; - Optional drag_shadow_color; - - Params() - : label("label"), - drag_highlight_color("drag_highlight_color", LLUIColorTable::instance().getColor("DefaultHighlightLight")), - drag_shadow_color("drag_shadow_color", LLUIColorTable::instance().getColor("DefaultShadowDark")) - { - changeDefault(mouse_opaque, true); - changeDefault(follows.flags, FOLLOWS_ALL); - } - }; - void initFromParams(const Params&); - - virtual ~LLDragHandle(); - - virtual void setValue(const LLSD& value); - - void setForeground(bool b) { mForeground = b; } - bool getForeground() const { return mForeground; } - void setMaxTitleWidth(S32 max_width) {mMaxTitleWidth = llmin(max_width, mMaxTitleWidth); } - S32 getMaxTitleWidth() const { return mMaxTitleWidth; } - void setButtonsRect(const LLRect& rect){ mButtonsRect = rect; } - LLRect getButtonsRect() { return mButtonsRect; } - void setTitleVisible(bool visible); - - virtual void setTitle( const std::string& title ) = 0; - virtual std::string getTitle() const = 0; - - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - -protected: - LLDragHandle(const Params&); - friend class LLUICtrlFactory; - -protected: - LLTextBox* mTitleBox; - -private: - LLRect mButtonsRect; - S32 mDragLastScreenX; - S32 mDragLastScreenY; - S32 mLastMouseScreenX; - S32 mLastMouseScreenY; - LLCoordGL mLastMouseDir; - LLUIColor mDragHighlightColor; - LLUIColor mDragShadowColor; - S32 mMaxTitleWidth; - bool mForeground; - - // Pixels near the edge to snap floaters. - static S32 sSnapMargin; -}; - - -// Use this one for traditional top-of-window draggers -class LLDragHandleTop -: public LLDragHandle -{ -protected: - LLDragHandleTop(const Params& p) : LLDragHandle(p) {} - friend class LLUICtrlFactory; -public: - virtual void setTitle( const std::string& title ); - virtual std::string getTitle() const; - virtual void draw(); - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - -private: - void reshapeTitleBox(); -}; - - -// Use this for left-side, vertical text draggers -class LLDragHandleLeft -: public LLDragHandle -{ -protected: - LLDragHandleLeft(const Params& p) : LLDragHandle(p) {} - friend class LLUICtrlFactory; -public: - virtual void setTitle( const std::string& title ); - virtual std::string getTitle() const; - virtual void draw(); - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - -}; - -const S32 DRAG_HANDLE_HEIGHT = 16; -const S32 DRAG_HANDLE_WIDTH = 16; - -#endif // LL_DRAGHANDLE_H +/** + * @file lldraghandle.h + * @brief LLDragHandle base class + * + * $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$ + */ + +// A widget for dragging a view around the screen using the mouse. + +#ifndef LL_DRAGHANDLE_H +#define LL_DRAGHANDLE_H + +#include "llview.h" +#include "v4color.h" +#include "llrect.h" +#include "llcoord.h" + +class LLTextBox; + +class LLDragHandle : public LLView +{ +public: + struct Params + : public LLInitParam::Block + { + Optional label; + Optional drag_highlight_color; + Optional drag_shadow_color; + + Params() + : label("label"), + drag_highlight_color("drag_highlight_color", LLUIColorTable::instance().getColor("DefaultHighlightLight")), + drag_shadow_color("drag_shadow_color", LLUIColorTable::instance().getColor("DefaultShadowDark")) + { + changeDefault(mouse_opaque, true); + changeDefault(follows.flags, FOLLOWS_ALL); + } + }; + void initFromParams(const Params&); + + virtual ~LLDragHandle(); + + virtual void setValue(const LLSD& value); + + void setForeground(bool b) { mForeground = b; } + bool getForeground() const { return mForeground; } + void setMaxTitleWidth(S32 max_width) {mMaxTitleWidth = llmin(max_width, mMaxTitleWidth); } + S32 getMaxTitleWidth() const { return mMaxTitleWidth; } + void setButtonsRect(const LLRect& rect){ mButtonsRect = rect; } + LLRect getButtonsRect() { return mButtonsRect; } + void setTitleVisible(bool visible); + + virtual void setTitle( const std::string& title ) = 0; + virtual std::string getTitle() const = 0; + + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + +protected: + LLDragHandle(const Params&); + friend class LLUICtrlFactory; + +protected: + LLTextBox* mTitleBox; + +private: + LLRect mButtonsRect; + S32 mDragLastScreenX; + S32 mDragLastScreenY; + S32 mLastMouseScreenX; + S32 mLastMouseScreenY; + LLCoordGL mLastMouseDir; + LLUIColor mDragHighlightColor; + LLUIColor mDragShadowColor; + S32 mMaxTitleWidth; + bool mForeground; + + // Pixels near the edge to snap floaters. + static S32 sSnapMargin; +}; + + +// Use this one for traditional top-of-window draggers +class LLDragHandleTop +: public LLDragHandle +{ +protected: + LLDragHandleTop(const Params& p) : LLDragHandle(p) {} + friend class LLUICtrlFactory; +public: + virtual void setTitle( const std::string& title ); + virtual std::string getTitle() const; + virtual void draw(); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + +private: + void reshapeTitleBox(); +}; + + +// Use this for left-side, vertical text draggers +class LLDragHandleLeft +: public LLDragHandle +{ +protected: + LLDragHandleLeft(const Params& p) : LLDragHandle(p) {} + friend class LLUICtrlFactory; +public: + virtual void setTitle( const std::string& title ); + virtual std::string getTitle() const; + virtual void draw(); + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + +}; + +const S32 DRAG_HANDLE_HEIGHT = 16; +const S32 DRAG_HANDLE_WIDTH = 16; + +#endif // LL_DRAGHANDLE_H diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h index 3770502058..ff111c16a7 100644 --- a/indra/llui/lleditmenuhandler.h +++ b/indra/llui/lleditmenuhandler.h @@ -1,71 +1,71 @@ -/** -* @file lleditmenuhandler.h -* @authors Aaron Yonas, James Cook -* -* $LicenseInfo:firstyear=2006&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 LLEDITMENUHANDLER_H -#define LLEDITMENUHANDLER_H - -// Interface used by menu system for plug-in hotkey/menu handling -class LLEditMenuHandler -{ -public: - // this is needed even though this is just an interface class. - virtual ~LLEditMenuHandler(); - - virtual void undo() {}; - virtual bool canUndo() const { return false; } - - virtual void redo() {}; - virtual bool canRedo() const { return false; } - - virtual void cut() {}; - virtual bool canCut() const { return false; } - - virtual void copy() {}; - virtual bool canCopy() const { return false; } - - virtual void paste() {}; - virtual bool canPaste() const { return false; } - - // "delete" is a keyword - virtual void doDelete() {}; - virtual bool canDoDelete() const { return false; } - - virtual void selectAll() {}; - virtual bool canSelectAll() const { return false; } - - virtual void deselect() {}; - virtual bool canDeselect() const { return false; } - - // TODO: Instead of being a public data member, it would be better to hide it altogether - // and have a "set" method and then a bunch of static versions of the cut, copy, paste - // methods, etc that operate on the current global instance. That would drastically - // simplify the existing code that accesses this global variable by putting all the - // null checks in the one implementation of those static methods. -MG - static LLEditMenuHandler* gEditMenuHandler; -}; - - -#endif +/** +* @file lleditmenuhandler.h +* @authors Aaron Yonas, James Cook +* +* $LicenseInfo:firstyear=2006&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 LLEDITMENUHANDLER_H +#define LLEDITMENUHANDLER_H + +// Interface used by menu system for plug-in hotkey/menu handling +class LLEditMenuHandler +{ +public: + // this is needed even though this is just an interface class. + virtual ~LLEditMenuHandler(); + + virtual void undo() {}; + virtual bool canUndo() const { return false; } + + virtual void redo() {}; + virtual bool canRedo() const { return false; } + + virtual void cut() {}; + virtual bool canCut() const { return false; } + + virtual void copy() {}; + virtual bool canCopy() const { return false; } + + virtual void paste() {}; + virtual bool canPaste() const { return false; } + + // "delete" is a keyword + virtual void doDelete() {}; + virtual bool canDoDelete() const { return false; } + + virtual void selectAll() {}; + virtual bool canSelectAll() const { return false; } + + virtual void deselect() {}; + virtual bool canDeselect() const { return false; } + + // TODO: Instead of being a public data member, it would be better to hide it altogether + // and have a "set" method and then a bunch of static versions of the cut, copy, paste + // methods, etc that operate on the current global instance. That would drastically + // simplify the existing code that accesses this global variable by putting all the + // null checks in the one implementation of those static methods. -MG + static LLEditMenuHandler* gEditMenuHandler; +}; + + +#endif diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp index 1311914131..f7b3a1e9a6 100644 --- a/indra/llui/llfiltereditor.cpp +++ b/indra/llui/llfiltereditor.cpp @@ -1,46 +1,46 @@ -/** - * @file llfiltereditor.cpp - * @brief LLFilterEditor 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$ - */ - -// Text editor widget to let users enter a single line. - -#include "linden_common.h" - -#include "llfiltereditor.h" - -LLFilterEditor::LLFilterEditor(const LLFilterEditor::Params& p) -: LLSearchEditor(p) -{ - setCommitOnFocusLost(false); // we'll commit on every keystroke, don't re-commit when we take focus away (i.e. we go to interact with the actual results!) -} - - -void LLFilterEditor::handleKeystroke() -{ - this->LLSearchEditor::handleKeystroke(); - - // Commit on every keystroke. - onCommit(); -} +/** + * @file llfiltereditor.cpp + * @brief LLFilterEditor 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$ + */ + +// Text editor widget to let users enter a single line. + +#include "linden_common.h" + +#include "llfiltereditor.h" + +LLFilterEditor::LLFilterEditor(const LLFilterEditor::Params& p) +: LLSearchEditor(p) +{ + setCommitOnFocusLost(false); // we'll commit on every keystroke, don't re-commit when we take focus away (i.e. we go to interact with the actual results!) +} + + +void LLFilterEditor::handleKeystroke() +{ + this->LLSearchEditor::handleKeystroke(); + + // Commit on every keystroke. + onCommit(); +} diff --git a/indra/llui/llflashtimer.cpp b/indra/llui/llflashtimer.cpp index 1506279232..c3db24c987 100644 --- a/indra/llui/llflashtimer.cpp +++ b/indra/llui/llflashtimer.cpp @@ -1,98 +1,98 @@ -/** - * @file llflashtimer.cpp - * @brief LLFlashTimer class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, 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 "llflashtimer.h" -#include "lleventtimer.h" -#include "llui.h" - -LLFlashTimer::LLFlashTimer(callback_t cb, S32 count, F32 period) -: LLEventTimer(period), - mCallback(cb), - mCurrentTickCount(0), - mIsFlashingInProgress(false), - mIsCurrentlyHighlighted(false), - mUnset(false) -{ - mEventTimer.stop(); - - // By default use settings from settings.xml to be able change them via Debug settings. See EXT-5973. - // Due to Timer is implemented as derived class from EventTimer it is impossible to change period - // in runtime. So, both settings are made as required restart. - mFlashCount = 2 * ((count > 0) ? count : LLUI::getInstance()->mSettingGroups["config"]->getS32("FlashCount")); - if (mPeriod <= 0) - { - mPeriod = LLUI::getInstance()->mSettingGroups["config"]->getF32("FlashPeriod"); - } -} - -void LLFlashTimer::unset() -{ - mUnset = true; - mCallback = NULL; -} - -bool LLFlashTimer::tick() -{ - mIsCurrentlyHighlighted = !mIsCurrentlyHighlighted; - - if (mCallback) - { - mCallback(mIsCurrentlyHighlighted); - } - - if (++mCurrentTickCount >= mFlashCount) - { - stopFlashing(); - } - - return mUnset; -} - -void LLFlashTimer::startFlashing() -{ - mIsFlashingInProgress = true; - mIsCurrentlyHighlighted = true; - mEventTimer.start(); -} - -void LLFlashTimer::stopFlashing() -{ - mEventTimer.stop(); - mIsFlashingInProgress = false; - mIsCurrentlyHighlighted = false; - mCurrentTickCount = 0; -} - -bool LLFlashTimer::isFlashingInProgress() -{ - return mIsFlashingInProgress; -} - -bool LLFlashTimer::isCurrentlyHighlighted() -{ - return mIsCurrentlyHighlighted; -} - - +/** + * @file llflashtimer.cpp + * @brief LLFlashTimer class implementation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llflashtimer.h" +#include "lleventtimer.h" +#include "llui.h" + +LLFlashTimer::LLFlashTimer(callback_t cb, S32 count, F32 period) +: LLEventTimer(period), + mCallback(cb), + mCurrentTickCount(0), + mIsFlashingInProgress(false), + mIsCurrentlyHighlighted(false), + mUnset(false) +{ + mEventTimer.stop(); + + // By default use settings from settings.xml to be able change them via Debug settings. See EXT-5973. + // Due to Timer is implemented as derived class from EventTimer it is impossible to change period + // in runtime. So, both settings are made as required restart. + mFlashCount = 2 * ((count > 0) ? count : LLUI::getInstance()->mSettingGroups["config"]->getS32("FlashCount")); + if (mPeriod <= 0) + { + mPeriod = LLUI::getInstance()->mSettingGroups["config"]->getF32("FlashPeriod"); + } +} + +void LLFlashTimer::unset() +{ + mUnset = true; + mCallback = NULL; +} + +bool LLFlashTimer::tick() +{ + mIsCurrentlyHighlighted = !mIsCurrentlyHighlighted; + + if (mCallback) + { + mCallback(mIsCurrentlyHighlighted); + } + + if (++mCurrentTickCount >= mFlashCount) + { + stopFlashing(); + } + + return mUnset; +} + +void LLFlashTimer::startFlashing() +{ + mIsFlashingInProgress = true; + mIsCurrentlyHighlighted = true; + mEventTimer.start(); +} + +void LLFlashTimer::stopFlashing() +{ + mEventTimer.stop(); + mIsFlashingInProgress = false; + mIsCurrentlyHighlighted = false; + mCurrentTickCount = 0; +} + +bool LLFlashTimer::isFlashingInProgress() +{ + return mIsFlashingInProgress; +} + +bool LLFlashTimer::isCurrentlyHighlighted() +{ + return mIsCurrentlyHighlighted; +} + + diff --git a/indra/llui/llflashtimer.h b/indra/llui/llflashtimer.h index 6da6f55deb..b55ce53fc0 100644 --- a/indra/llui/llflashtimer.h +++ b/indra/llui/llflashtimer.h @@ -1,74 +1,74 @@ -/** - * @file llflashtimer.h - * @brief LLFlashTimer class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, 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_FLASHTIMER_H -#define LL_FLASHTIMER_H - -#include "lleventtimer.h" -#include "boost/function.hpp" - -class LLFlashTimer : public LLEventTimer -{ -public: - - typedef boost::function callback_t; - - /** - * Constructor. - * - * @param count - how many times callback should be called (twice to not change original state) - * @param period - how frequently callback should be called - * @param cb - callback to be called each tick - */ - LLFlashTimer(callback_t cb = NULL, S32 count = 0, F32 period = 0.0); - ~LLFlashTimer() {}; - - /*virtual*/ bool tick(); - - void startFlashing(); - void stopFlashing(); - - bool isFlashingInProgress(); - bool isCurrentlyHighlighted(); - /* - * Use this instead of deleting this object. - * The next call to tick() will return true and that will destroy this object. - */ - void unset(); - -private: - callback_t mCallback; - /** - * How many times parent will blink. - */ - S32 mFlashCount; - S32 mCurrentTickCount; - bool mIsCurrentlyHighlighted; - bool mIsFlashingInProgress; - bool mUnset; -}; - -#endif /* LL_FLASHTIMER_H */ +/** + * @file llflashtimer.h + * @brief LLFlashTimer class implementation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_FLASHTIMER_H +#define LL_FLASHTIMER_H + +#include "lleventtimer.h" +#include "boost/function.hpp" + +class LLFlashTimer : public LLEventTimer +{ +public: + + typedef boost::function callback_t; + + /** + * Constructor. + * + * @param count - how many times callback should be called (twice to not change original state) + * @param period - how frequently callback should be called + * @param cb - callback to be called each tick + */ + LLFlashTimer(callback_t cb = NULL, S32 count = 0, F32 period = 0.0); + ~LLFlashTimer() {}; + + /*virtual*/ bool tick(); + + void startFlashing(); + void stopFlashing(); + + bool isFlashingInProgress(); + bool isCurrentlyHighlighted(); + /* + * Use this instead of deleting this object. + * The next call to tick() will return true and that will destroy this object. + */ + void unset(); + +private: + callback_t mCallback; + /** + * How many times parent will blink. + */ + S32 mFlashCount; + S32 mCurrentTickCount; + bool mIsCurrentlyHighlighted; + bool mIsFlashingInProgress; + bool mUnset; +}; + +#endif /* LL_FLASHTIMER_H */ diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 67a0068b2f..b1a95715f1 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -1,1439 +1,1439 @@ -/** - * @file llflatlistview.cpp - * @brief LLFlatListView base class and extension to support messages for several cases of an empty list. - * - * $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 "llpanel.h" -#include "lltextbox.h" - -#include "llflatlistview.h" - -static const LLDefaultChildRegistry::Register flat_list_view("flat_list_view"); - -const LLSD SELECTED_EVENT = LLSD().with("selected", true); -const LLSD UNSELECTED_EVENT = LLSD().with("selected", false); - -//forward declaration -bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2); - -LLFlatListView::Params::Params() -: item_pad("item_pad"), - allow_select("allow_select"), - multi_select("multi_select"), - keep_one_selected("keep_one_selected"), - keep_selection_visible_on_reshape("keep_selection_visible_on_reshape",false), - no_items_text("no_items_text") -{}; - -void LLFlatListView::reshape(S32 width, S32 height, bool called_from_parent /* = true */) -{ - S32 delta = height - getRect().getHeight(); - LLScrollContainer::reshape(width, height, called_from_parent); - setItemsNoScrollWidth(width); - rearrangeItems(); - - if(delta!= 0 && mKeepSelectionVisibleOnReshape) - { - ensureSelectedVisible(); - } -} - -const LLRect& LLFlatListView::getItemsRect() const -{ - return mItemsPanel->getRect(); -} - -bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/) -{ - if (!item) return false; - if (value.isUndefined()) return false; - - //force uniqueness of items, easiest check but unreliable - if (item->getParent() == mItemsPanel) return false; - - item_pair_t* new_pair = new item_pair_t(item, value); - switch (pos) - { - case ADD_TOP: - mItemPairs.push_front(new_pair); - //in LLView::draw() children are iterated in backorder - mItemsPanel->addChildInBack(item); - break; - case ADD_BOTTOM: - mItemPairs.push_back(new_pair); - mItemsPanel->addChild(item); - break; - default: - break; - } - - //_4 is for MASK - item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); - item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4)); - - // Children don't accept the focus - item->setTabStop(false); - - if (rearrange) - { - rearrangeItems(); - notifyParentItemsRectChanged(); - } - return true; -} - -bool LLFlatListView::addItemPairs(pairs_list_t panel_list, bool rearrange /*= true*/) -{ - if (!mItemComparator) - { - LL_WARNS_ONCE() << "No comparator specified for inserting FlatListView items." << LL_ENDL; - return false; - } - if (panel_list.size() == 0) - { - return false; - } - - // presort list so that it will be easier to sort elements into mItemPairs - panel_list.sort(ComparatorAdaptor(*mItemComparator)); - - pairs_const_iterator_t new_pair_it = panel_list.begin(); - item_pair_t* new_pair = *new_pair_it; - pairs_iterator_t pair_it = mItemPairs.begin(); - item_pair_t* item_pair = *pair_it; - - // sort panel_list into mItemPars - while (new_pair_it != panel_list.end() && pair_it != mItemPairs.end()) - { - if (!new_pair->first || new_pair->first->getParent() == mItemsPanel) - { - // iterator already used or we are reusing existing panel - new_pair_it++; - new_pair = *new_pair_it; - } - else if (mItemComparator->compare(new_pair->first, item_pair->first)) - { - LLPanel* panel = new_pair->first; - - mItemPairs.insert(pair_it, new_pair); - mItemsPanel->addChild(panel); - - //_4 is for MASK - panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); - panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4)); - // Children don't accept the focus - panel->setTabStop(false); - } - else - { - pair_it++; - item_pair = *pair_it; - } - } - - // Add what is left of panel_list into the end of mItemPairs. - for (; new_pair_it != panel_list.end(); ++new_pair_it) - { - item_pair_t* item_pair = *new_pair_it; - LLPanel *panel = item_pair->first; - if (panel && panel->getParent() != mItemsPanel) - { - mItemPairs.push_back(item_pair); - mItemsPanel->addChild(panel); - - //_4 is for MASK - panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, item_pair, _4)); - panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, item_pair, _4)); - // Children don't accept the focus - panel->setTabStop(false); - } - } - - if (rearrange) - { - rearrangeItems(); - notifyParentItemsRectChanged(); - } - return true; -} - - -bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/) -{ - if (!after_item) return false; - if (!item_to_add) return false; - if (value.isUndefined()) return false; - - if (mItemPairs.empty()) return false; - - //force uniqueness of items, easiest check but unreliable - if (item_to_add->getParent() == mItemsPanel) return false; - - item_pair_t* after_pair = getItemPair(after_item); - if (!after_pair) return false; - - item_pair_t* new_pair = new item_pair_t(item_to_add, value); - if (after_pair == mItemPairs.back()) - { - mItemPairs.push_back(new_pair); - mItemsPanel->addChild(item_to_add); - } - else - { - pairs_iterator_t it = mItemPairs.begin(); - for (; it != mItemPairs.end(); ++it) - { - if (*it == after_pair) - { - // insert new elements before the element at position of passed iterator. - mItemPairs.insert(++it, new_pair); - mItemsPanel->addChild(item_to_add); - break; - } - } - } - - //_4 is for MASK - item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); - item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4)); - - rearrangeItems(); - notifyParentItemsRectChanged(); - return true; -} - - -bool LLFlatListView::removeItem(LLPanel* item, bool rearrange) -{ - if (!item) return false; - if (item->getParent() != mItemsPanel) return false; - - item_pair_t* item_pair = getItemPair(item); - if (!item_pair) return false; - - return removeItemPair(item_pair, rearrange); -} - -bool LLFlatListView::removeItemByValue(const LLSD& value, bool rearrange) -{ - if (value.isUndefined()) return false; - - item_pair_t* item_pair = getItemPair(value); - if (!item_pair) return false; - - return removeItemPair(item_pair, rearrange); -} - -bool LLFlatListView::removeItemByUUID(const LLUUID& uuid, bool rearrange) -{ - return removeItemByValue(LLSD(uuid), rearrange); -} - -LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const -{ - if (value.isUndefined()) return NULL; - - item_pair_t* pair = getItemPair(value); - if (pair) return pair->first; - return NULL; -} - -bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/) -{ - if (!item) return false; - if (item->getParent() != mItemsPanel) return false; - - item_pair_t* item_pair = getItemPair(item); - if (!item_pair) return false; - - return selectItemPair(item_pair, select); -} - -bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/) -{ - if (value.isUndefined()) return false; - - item_pair_t* item_pair = getItemPair(value); - if (!item_pair) return false; - - return selectItemPair(item_pair, select); -} - -bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/) -{ - return selectItemByValue(LLSD(uuid), select); -} - - -LLSD LLFlatListView::getSelectedValue() const -{ - if (mSelectedItemPairs.empty()) return LLSD(); - - item_pair_t* first_selected_pair = mSelectedItemPairs.front(); - return first_selected_pair->second; -} - -void LLFlatListView::getSelectedValues(std::vector& selected_values) const -{ - if (mSelectedItemPairs.empty()) return; - - for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) - { - selected_values.push_back((*it)->second); - } -} - -LLUUID LLFlatListView::getSelectedUUID() const -{ - const LLSD& value = getSelectedValue(); - if (value.isDefined() && value.isUUID()) - { - return value.asUUID(); - } - else - { - return LLUUID::null; - } -} - -void LLFlatListView::getSelectedUUIDs(uuid_vec_t& selected_uuids) const -{ - if (mSelectedItemPairs.empty()) return; - - for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) - { - selected_uuids.push_back((*it)->second.asUUID()); - } -} - -LLPanel* LLFlatListView::getSelectedItem() const -{ - if (mSelectedItemPairs.empty()) return NULL; - - return mSelectedItemPairs.front()->first; -} - -void LLFlatListView::getSelectedItems(std::vector& selected_items) const -{ - if (mSelectedItemPairs.empty()) return; - - for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) - { - selected_items.push_back((*it)->first); - } -} - -void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/) -{ - if (mSelectedItemPairs.empty()) return; - - for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) - { - item_pair_t* pair_to_deselect = *it; - LLPanel* item = pair_to_deselect->first; - item->setValue(UNSELECTED_EVENT); - } - - mSelectedItemPairs.clear(); - - if (mCommitOnSelectionChange && !no_commit_on_deselection) - { - onCommit(); - } - - // Stretch selected item rect to ensure it won't be clipped - mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); -} - -void LLFlatListView::setNoItemsCommentText(const std::string& comment_text) -{ - mNoItemsCommentTextbox->setValue(comment_text); -} - -U32 LLFlatListView::size(const bool only_visible_items) const -{ - if (only_visible_items) - { - U32 size = 0; - for (pairs_const_iterator_t - iter = mItemPairs.begin(), - iter_end = mItemPairs.end(); - iter != iter_end; ++iter) - { - if ((*iter)->first->getVisible()) - ++size; - } - return size; - } - else - { - return mItemPairs.size(); - } -} - -void LLFlatListView::clear() -{ - // This will clear mSelectedItemPairs, calling all appropriate callbacks. - resetSelection(); - - // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex. - for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - mItemsPanel->removeChild((*it)->first); - (*it)->first->die(); - delete *it; - } - mItemPairs.clear(); - - // also set items panel height to zero. Reshape it to allow reshaping of non-item children - LLRect rc = mItemsPanel->getRect(); - rc.mBottom = rc.mTop; - mItemsPanel->reshape(rc.getWidth(), rc.getHeight()); - mItemsPanel->setRect(rc); - - setNoItemsCommentVisible(true); - notifyParentItemsRectChanged(); -} - -void LLFlatListView::sort() -{ - if (!mItemComparator) - { - LL_WARNS() << "No comparator specified for sorting FlatListView items." << LL_ENDL; - return; - } - - mItemPairs.sort(ComparatorAdaptor(*mItemComparator)); - rearrangeItems(); -} - -bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value) -{ - if (old_value.isUndefined() || new_value.isUndefined()) return false; - if (llsds_are_equal(old_value, new_value)) return false; - - item_pair_t* item_pair = getItemPair(old_value); - if (!item_pair) return false; - - item_pair->second = new_value; - return true; -} - -////////////////////////////////////////////////////////////////////////// -// PROTECTED STUFF -////////////////////////////////////////////////////////////////////////// - -LLFlatListView::LLFlatListView(const LLFlatListView::Params& p) -: LLScrollContainer(p) - , mItemComparator(NULL) - , mItemsPanel(NULL) - , mItemPad(p.item_pad) - , mAllowSelection(p.allow_select) - , mMultipleSelection(p.multi_select) - , mKeepOneItemSelected(p.keep_one_selected) - , mCommitOnSelectionChange(false) - , mPrevNotifyParentRect(LLRect()) - , mNoItemsCommentTextbox(NULL) - , mIsConsecutiveSelection(false) - , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape) -{ - mBorderThickness = getBorderWidth(); - - LLRect scroll_rect = getRect(); - LLRect items_rect; - - setItemsNoScrollWidth(scroll_rect.getWidth()); - items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0); - - LLPanel::Params pp; - pp.rect(items_rect); - mItemsPanel = LLUICtrlFactory::create (pp); - addChild(mItemsPanel); - - //we don't need to stretch in vertical direction on reshaping by a parent - //no bottom following! - mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP); - - LLViewBorder::Params params; - params.name("scroll border"); - params.rect(getLastSelectedItemRect()); - params.visible(false); - params.bevel_style(LLViewBorder::BEVEL_IN); - mSelectedItemsBorder = LLUICtrlFactory::create (params); - mItemsPanel->addChild( mSelectedItemsBorder ); - - { - // create textbox for "No Items" comment text - LLTextBox::Params text_p = p.no_items_text; - if (!text_p.rect.isProvided()) - { - LLRect comment_rect = getRect(); - comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight()); - comment_rect.stretch(-getBorderWidth()); - text_p.rect(comment_rect); - } - text_p.border_visible(false); - - if (!text_p.follows.isProvided()) - { - text_p.follows.flags(FOLLOWS_ALL); - } - mNoItemsCommentTextbox = LLUICtrlFactory::create(text_p, this); - } -}; - -LLFlatListView::~LLFlatListView() -{ - for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - mItemsPanel->removeChild((*it)->first); - (*it)->first->die(); - delete *it; - } - mItemPairs.clear(); -} - -// virtual -void LLFlatListView::draw() -{ - // Highlight border if a child of this container has keyboard focus - if( mSelectedItemsBorder->getVisible() ) - { - mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() ); - } - LLScrollContainer::draw(); -} - -// virtual -bool LLFlatListView::postBuild() -{ - setTabStop(true); - return LLScrollContainer::postBuild(); -} - -void LLFlatListView::rearrangeItems() -{ - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - setNoItemsCommentVisible(0==size()); - - if (mItemPairs.empty()) return; - - //calculating required height - assuming items can be of different height - //list should accommodate all its items - S32 height = 0; - - S32 invisible_children_count = 0; - pairs_iterator_t it = mItemPairs.begin(); - for (; it != mItemPairs.end(); ++it) - { - LLPanel* item = (*it)->first; - - // skip invisible child - if (!item->getVisible()) - { - ++invisible_children_count; - continue; - } - - height += item->getRect().getHeight(); - } - - // add paddings between items, excluding invisible ones - height += mItemPad * (mItemPairs.size() - invisible_children_count - 1); - - LLRect rc = mItemsPanel->getRect(); - S32 width = mItemsNoScrollWidth; - - // update width to avoid horizontal scrollbar - if (height > getRect().getHeight() - 2 * mBorderThickness) - width -= scrollbar_size; - - //changes the bottom, end of the list goes down in the scroll container - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height); - mItemsPanel->setRect(rc); - - //reshaping items - S32 item_new_top = height; - pairs_iterator_t it2, first_it = mItemPairs.begin(); - for (it2 = first_it; it2 != mItemPairs.end(); ++it2) - { - LLPanel* item = (*it2)->first; - - // skip invisible child - if (!item->getVisible()) - continue; - - LLRect rc = item->getRect(); - rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight()); - item->reshape(rc.getWidth(), rc.getHeight()); - item->setRect(rc); - - // move top for next item in list - item_new_top -= (rc.getHeight() + mItemPad); - } - - // Stretch selected item rect to ensure it won't be clipped - mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); -} - -void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) -{ - if (!item_pair) return; - - if (!item_pair->first) - { - LL_WARNS() << "Attempt to selet an item pair containing null panel item" << LL_ENDL; - return; - } - - setFocus(true); - - bool select_item = !isSelected(item_pair); - - //*TODO find a better place for that enforcing stuff - if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return; - - if ( (mask & MASK_SHIFT) && !(mask & MASK_CONTROL) - && mMultipleSelection && !mSelectedItemPairs.empty() ) - { - item_pair_t* last_selected_pair = mSelectedItemPairs.back(); - - // If item_pair is already selected - do nothing - if (last_selected_pair == item_pair) - return; - - bool grab_items = false; - bool reverse = false; - pairs_list_t pairs_to_select; - - // Pick out items from list between last selected and current clicked item_pair. - for (pairs_iterator_t - iter = mItemPairs.begin(), - iter_end = mItemPairs.end(); - iter != iter_end; ++iter) - { - item_pair_t* cur = *iter; - if (cur == last_selected_pair || cur == item_pair) - { - // We've got reverse selection if last grabed item isn't a new selection. - reverse = grab_items && (cur != item_pair); - grab_items = !grab_items; - // Skip last selected and current clicked item pairs. - continue; - } - if (!cur->first->getVisible()) - { - // Skip invisible item pairs. - continue; - } - if (grab_items) - { - pairs_to_select.push_back(cur); - } - } - - if (reverse) - { - pairs_to_select.reverse(); - } - - pairs_to_select.push_back(item_pair); - - for (pairs_iterator_t - iter = pairs_to_select.begin(), - iter_end = pairs_to_select.end(); - iter != iter_end; ++iter) - { - item_pair_t* pair_to_select = *iter; - if (isSelected(pair_to_select)) - { - // Item was already selected but there is a need to keep order from last selected pair to new selection. - // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair(). - mSelectedItemPairs.remove(pair_to_select); - mSelectedItemPairs.push_back(pair_to_select); - } - else - { - selectItemPair(pair_to_select, true); - } - } - - if (!select_item) - { - // Update last selected item border. - mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); - } - return; - } - - //no need to do additional commit on selection reset - if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(true); - - //only CTRL usage allows to deselect an item, usual clicking on an item cannot deselect it - if (mask & MASK_CONTROL) - selectItemPair(item_pair, select_item); - else - selectItemPair(item_pair, true); -} - -void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask) -{ - if (!item_pair) - return; - - // Forbid deselecting of items on right mouse button click if mMultipleSelection flag is set on, - // because some of derived classes may have context menu and selected items must be kept. - if ( !(mask & MASK_CONTROL) && mMultipleSelection && isSelected(item_pair) ) - return; - - // else got same behavior as at onItemMouseClick - onItemMouseClick(item_pair, mask); -} - -bool LLFlatListView::handleKeyHere(KEY key, MASK mask) -{ - bool reset_selection = (mask != MASK_SHIFT); - bool handled = false; - switch (key) - { - case KEY_RETURN: - { - if (mSelectedItemPairs.size() && mask == MASK_NONE) - { - mOnReturnSignal(this, getValue()); - handled = true; - } - break; - } - case KEY_UP: - { - if ( !selectNextItemPair(true, reset_selection) && reset_selection) - { - // 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 - resetSelection(); - } - break; - } - case KEY_DOWN: - { - if ( !selectNextItemPair(false, reset_selection) && reset_selection) - { - // If case we are in accordion tab notify parent to go to the next accordion - if( notifyParent(LLSD().with("action","select_next")) > 0 ) //message was processed - resetSelection(); - } - break; - } - case KEY_ESCAPE: - { - if (mask == MASK_NONE) - { - setFocus(false); // pass focus to the game area (EXT-8357) - } - break; - } - default: - break; - } - - if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() ) - { - ensureSelectedVisible(); - /* - LLRect visible_rc = getVisibleContentRect(); - LLRect selected_rc = getLastSelectedItemRect(); - - if ( !visible_rc.contains (selected_rc) ) - { - // But scroll in Items panel coordinates - scrollToShowRect(selected_rc); - } - - // In case we are in accordion tab notify parent to show selected rectangle - LLRect screen_rc; - localRectToScreen(selected_rc, &screen_rc); - notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/ - - handled = true; - } - - return handled ? handled : LLScrollContainer::handleKeyHere(key, mask); -} - -LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const -{ - llassert(item); - - for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - item_pair_t* item_pair = *it; - if (item_pair->first == item) return item_pair; - } - return NULL; -} - -//compares two LLSD's -bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2) -{ - llassert(llsd_1.isDefined()); - llassert(llsd_2.isDefined()); - - if (llsd_1.type() != llsd_2.type()) return false; - - if (!llsd_1.isMap()) - { - if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID(); - - //assumptions that string representaion is enough for other types - return llsd_1.asString() == llsd_2.asString(); - } - - if (llsd_1.size() != llsd_2.size()) return false; - - LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap(); - LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap(); - for (S32 i = 0; i < llsd_1.size(); ++i) - { - if ((*llsd_1_it).first != (*llsd_2_it).first) return false; - if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false; - ++llsd_1_it; - ++llsd_2_it; - } - return true; -} - -LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const -{ - llassert(value.isDefined()); - - for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - item_pair_t* item_pair = *it; - if (llsds_are_equal(item_pair->second, value)) return item_pair; - } - return NULL; -} - -bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select) -{ - llassert(item_pair); - - if (!mAllowSelection && select) return false; - - if (isSelected(item_pair) == select) return true; //already in specified selection state - if (select) - { - mSelectedItemPairs.push_back(item_pair); - } - else - { - mSelectedItemPairs.remove(item_pair); - } - - //a way of notifying panel of selection state changes - LLPanel* item = item_pair->first; - item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT); - - if (mCommitOnSelectionChange) - { - onCommit(); - } - - // Stretch selected item rect to ensure it won't be clipped - mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); - // By default mark it as not consecutive selection - mIsConsecutiveSelection = false; - - return true; -} - -void LLFlatListView::scrollToShowFirstSelectedItem() -{ - if (!mSelectedItemPairs.size()) return; - - LLRect selected_rc = mSelectedItemPairs.front()->first->getRect(); - - if (selected_rc.isValid()) - { - scrollToShowRect(selected_rc); - } -} - -LLRect LLFlatListView::getLastSelectedItemRect() -{ - if (!mSelectedItemPairs.size()) - { - return LLRect::null; - } - - return mSelectedItemPairs.back()->first->getRect(); -} - -void LLFlatListView::selectFirstItem () -{ - // No items - no actions! - if (0 == size()) return; - - // Select first visible item - for (pairs_iterator_t - iter = mItemPairs.begin(), - iter_end = mItemPairs.end(); - iter != iter_end; ++iter) - { - // skip invisible items - if ( (*iter)->first->getVisible() ) - { - selectItemPair(*iter, true); - ensureSelectedVisible(); - break; - } - } -} - -void LLFlatListView::selectLastItem () -{ - // No items - no actions! - if (0 == size()) return; - - // Select last visible item - for (pairs_list_t::reverse_iterator - r_iter = mItemPairs.rbegin(), - r_iter_end = mItemPairs.rend(); - r_iter != r_iter_end; ++r_iter) - { - // skip invisible items - if ( (*r_iter)->first->getVisible() ) - { - selectItemPair(*r_iter, true); - ensureSelectedVisible(); - break; - } - } -} - -void LLFlatListView::ensureSelectedVisible() -{ - LLRect selected_rc = getLastSelectedItemRect(); - - if ( selected_rc.isValid() ) - { - scrollToShowRect(selected_rc); - } -} - - -// virtual -bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection) -{ - // No items - no actions! - if ( 0 == size() ) - return false; - - if (!mIsConsecutiveSelection) - { - // Leave only one item selected if list has not consecutive selection - if (mSelectedItemPairs.size() && !reset_selection) - { - item_pair_t* cur_sel_pair = mSelectedItemPairs.back(); - resetSelection(); - selectItemPair (cur_sel_pair, true); - } - } - - if ( mSelectedItemPairs.size() ) - { - item_pair_t* to_sel_pair = NULL; - item_pair_t* cur_sel_pair = NULL; - - // Take the last selected pair - cur_sel_pair = mSelectedItemPairs.back(); - // Bases on given direction choose next item to select - if ( is_up_direction ) - { - // Find current selected item position in mItemPairs list - pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair); - - for (;++sel_it != mItemPairs.rend();) - { - // skip invisible items - if ( (*sel_it)->first->getVisible() ) - { - to_sel_pair = *sel_it; - break; - } - } - } - else - { - // Find current selected item position in mItemPairs list - pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair); - - for (;++sel_it != mItemPairs.end();) - { - // skip invisible items - if ( (*sel_it)->first->getVisible() ) - { - to_sel_pair = *sel_it; - break; - } - } - } - - if ( to_sel_pair ) - { - bool select = true; - if ( reset_selection ) - { - // Reset current selection if we were asked about it - resetSelection(); - } - else - { - // If item already selected and no reset request than we should deselect last selected item. - select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair)); - } - // Select/Deselect next item - selectItemPair(select ? to_sel_pair : cur_sel_pair, select); - // Mark it as consecutive selection - mIsConsecutiveSelection = true; - return true; - } - } - else - { - // If there weren't selected items then choose the first one bases on given direction - // Force selection to first item - if (is_up_direction) - selectLastItem(); - else - selectFirstItem(); - // Mark it as consecutive selection - mIsConsecutiveSelection = true; - return true; - } - - return false; -} - -bool LLFlatListView::canSelectAll() const -{ - return 0 != size() && mAllowSelection && mMultipleSelection; -} - -void LLFlatListView::selectAll() -{ - if (!mAllowSelection || !mMultipleSelection) - return; - - mSelectedItemPairs.clear(); - - for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - item_pair_t* item_pair = *it; - mSelectedItemPairs.push_back(item_pair); - //a way of notifying panel of selection state changes - LLPanel* item = item_pair->first; - item->setValue(SELECTED_EVENT); - } - - if (mCommitOnSelectionChange) - { - onCommit(); - } - - // Stretch selected item rect to ensure it won't be clipped - mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); -} - -bool LLFlatListView::isSelected(item_pair_t* item_pair) const -{ - llassert(item_pair); - - pairs_const_iterator_t it_end = mSelectedItemPairs.end(); - return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end; -} - -bool LLFlatListView::removeItemPair(item_pair_t* item_pair, bool rearrange) -{ - llassert(item_pair); - - bool deleted = false; - bool selection_changed = false; - for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - item_pair_t* _item_pair = *it; - if (_item_pair == item_pair) - { - mItemPairs.erase(it); - deleted = true; - break; - } - } - - if (!deleted) return false; - - for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) - { - item_pair_t* selected_item_pair = *it; - if (selected_item_pair == item_pair) - { - it = mSelectedItemPairs.erase(it); - selection_changed = true; - break; - } - } - - mItemsPanel->removeChild(item_pair->first); - item_pair->first->die(); - delete item_pair; - - if (rearrange) - { - rearrangeItems(); - notifyParentItemsRectChanged(); - } - - if (selection_changed && mCommitOnSelectionChange) - { - onCommit(); - } - - return true; -} - -void LLFlatListView::notifyParentItemsRectChanged() -{ - S32 comment_height = 0; - - // take into account comment text height if exists - if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible()) - { - // top text padding inside the textbox is included into the height - comment_height = mNoItemsCommentTextbox->getTextPixelHeight(); - - // take into account a distance from parent's top border to textbox's top - comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop; - } - - LLRect req_rect = getItemsRect(); - - // get maximum of items total height and comment text height - req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height)); - - // take into account border size. - req_rect.stretch(getBorderWidth()); - - if (req_rect == mPrevNotifyParentRect) - return; - - mPrevNotifyParentRect = req_rect; - - LLSD params; - params["action"] = "size_changes"; - params["width"] = req_rect.getWidth(); - params["height"] = req_rect.getHeight(); - - if (getParent()) // dummy widgets don't have a parent - getParent()->notifyParent(params); -} - -void LLFlatListView::setNoItemsCommentVisible(bool visible) const -{ - if (mNoItemsCommentTextbox) - { - mSelectedItemsBorder->setVisible(!visible); - mNoItemsCommentTextbox->setVisible(visible); - } -} - -void LLFlatListView::getItems(std::vector& items) const -{ - if (mItemPairs.empty()) return; - - items.clear(); - for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - items.push_back((*it)->first); - } -} - -void LLFlatListView::getValues(std::vector& values) const -{ - if (mItemPairs.empty()) return; - - values.clear(); - for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) - { - values.push_back((*it)->second); - } -} - -// virtual -void LLFlatListView::onFocusReceived() -{ - if (size()) - { - mSelectedItemsBorder->setVisible(true); - } - gEditMenuHandler = this; -} -// virtual -void LLFlatListView::onFocusLost() -{ - mSelectedItemsBorder->setVisible(false); - // Route menu back to the default - if (gEditMenuHandler == this) - { - gEditMenuHandler = NULL; - } -} - -//virtual -S32 LLFlatListView::notify(const LLSD& info) -{ - if (info.has("action")) - { - std::string str_action = info["action"]; - if (str_action == "select_first") - { - setFocus(true); - selectFirstItem(); - return 1; - } - else if (str_action == "select_last") - { - setFocus(true); - selectLastItem(); - return 1; - } - } - else if (info.has("rearrange")) - { - rearrangeItems(); - notifyParentItemsRectChanged(); - return 1; - } - - return 0; -} - -void LLFlatListView::detachItems(std::vector& detached_items) -{ - LLSD action; - action.with("detach", LLSD()); - // Clear detached_items list - detached_items.clear(); - // Go through items and detach valid items, remove them from items panel - // and add to detached_items. - pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); - while (iter != iter_end) - { - LLPanel* pItem = (*iter)->first; - if (1 == pItem->notify(action)) - { - selectItemPair((*iter), false); - mItemsPanel->removeChild(pItem); - detached_items.push_back(pItem); - } - iter++; - } - if (!detached_items.empty()) - { - // Some items were detached, clean ourself from unusable memory - if (detached_items.size() == mItemPairs.size()) - { - // This way will be faster if all items were disconnected - pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); - while (iter != iter_end) - { - (*iter)->first = NULL; - delete *iter; - iter++; - } - mItemPairs.clear(); - // Also set items panel height to zero. - // Reshape it to allow reshaping of non-item children. - LLRect rc = mItemsPanel->getRect(); - rc.mBottom = rc.mTop; - mItemsPanel->reshape(rc.getWidth(), rc.getHeight()); - mItemsPanel->setRect(rc); - setNoItemsCommentVisible(true); - } - else - { - std::vector::const_iterator - detached_iter = detached_items.begin(), - detached_iter_end = detached_items.end(); - while (detached_iter < detached_iter_end) - { - LLPanel* pDetachedItem = *detached_iter; - pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); - while (iter != iter_end) - { - item_pair_t* item_pair = *iter; - if (item_pair->first == pDetachedItem) - { - mItemPairs.erase(iter); - item_pair->first = NULL; - delete item_pair; - break; - } - iter++; - } - detached_iter++; - } - rearrangeItems(); - } - notifyParentItemsRectChanged(); - } -} - - -/************************************************************************/ -/* LLFlatListViewEx implementation */ -/************************************************************************/ -LLFlatListViewEx::Params::Params() -: no_items_msg("no_items_msg") -, no_filtered_items_msg("no_filtered_items_msg") -{ -} - -LLFlatListViewEx::LLFlatListViewEx(const Params& p) -: LLFlatListView(p) -, mNoFilteredItemsMsg(p.no_filtered_items_msg) -, mNoItemsMsg(p.no_items_msg) -, mForceShowingUnmatchedItems(false) -, mHasMatchedItems(false) -{ -} - -void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string) -{ - bool items_filtered = !filter_string.empty(); - if (items_filtered) - { - // items were filtered - LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(filter_string); - std::string text = mNoFilteredItemsMsg; - LLStringUtil::format(text, args); - setNoItemsCommentText(text); - } - else - { - // list does not contain any items at all - setNoItemsCommentText(mNoItemsMsg); - } -} - -bool LLFlatListViewEx::getForceShowingUnmatchedItems() -{ - return mForceShowingUnmatchedItems; -} - -void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show) -{ - mForceShowingUnmatchedItems = show; -} - -void LLFlatListViewEx::setFilterSubString(const std::string& filter_str, bool notify_parent) -{ - if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString)) - { - mFilterSubString = filter_str; - updateNoItemsMessage(mFilterSubString); - filterItems(false, notify_parent); - } -} - -bool LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action) -{ - if (!item) - return false; - - bool visible = true; - - // 0 signifies that filter is matched, - // i.e. we don't hide items that don't support 'match_filter' action, separators etc. - if (0 == item->notify(action)) - { - mHasMatchedItems = true; - } - else - { - // TODO: implement (re)storing of current selection. - if (!mForceShowingUnmatchedItems) - { - selectItem(item, false); - visible = false; - } - } - - if (item->getVisible() != visible) - { - item->setVisible(visible); - return true; - } - - return false; -} - -void LLFlatListViewEx::filterItems(bool re_sort, bool notify_parent) -{ - std::string cur_filter = mFilterSubString; - LLStringUtil::toUpper(cur_filter); - - LLSD action; - action.with("match_filter", cur_filter); - - mHasMatchedItems = false; - bool visibility_changed = false; - pairs_const_iterator_t iter = getItemPairs().begin(), iter_end = getItemPairs().end(); - while (iter != iter_end) - { - LLPanel* pItem = (*(iter++))->first; - visibility_changed |= updateItemVisibility(pItem, action); - } - - if (re_sort) - { - sort(); - } - - if (visibility_changed && notify_parent) - { - notifyParentItemsRectChanged(); - } -} - -bool LLFlatListViewEx::hasMatchedItems() -{ - return mHasMatchedItems; -} - -//EOF +/** + * @file llflatlistview.cpp + * @brief LLFlatListView base class and extension to support messages for several cases of an empty list. + * + * $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 "llpanel.h" +#include "lltextbox.h" + +#include "llflatlistview.h" + +static const LLDefaultChildRegistry::Register flat_list_view("flat_list_view"); + +const LLSD SELECTED_EVENT = LLSD().with("selected", true); +const LLSD UNSELECTED_EVENT = LLSD().with("selected", false); + +//forward declaration +bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2); + +LLFlatListView::Params::Params() +: item_pad("item_pad"), + allow_select("allow_select"), + multi_select("multi_select"), + keep_one_selected("keep_one_selected"), + keep_selection_visible_on_reshape("keep_selection_visible_on_reshape",false), + no_items_text("no_items_text") +{}; + +void LLFlatListView::reshape(S32 width, S32 height, bool called_from_parent /* = true */) +{ + S32 delta = height - getRect().getHeight(); + LLScrollContainer::reshape(width, height, called_from_parent); + setItemsNoScrollWidth(width); + rearrangeItems(); + + if(delta!= 0 && mKeepSelectionVisibleOnReshape) + { + ensureSelectedVisible(); + } +} + +const LLRect& LLFlatListView::getItemsRect() const +{ + return mItemsPanel->getRect(); +} + +bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/) +{ + if (!item) return false; + if (value.isUndefined()) return false; + + //force uniqueness of items, easiest check but unreliable + if (item->getParent() == mItemsPanel) return false; + + item_pair_t* new_pair = new item_pair_t(item, value); + switch (pos) + { + case ADD_TOP: + mItemPairs.push_front(new_pair); + //in LLView::draw() children are iterated in backorder + mItemsPanel->addChildInBack(item); + break; + case ADD_BOTTOM: + mItemPairs.push_back(new_pair); + mItemsPanel->addChild(item); + break; + default: + break; + } + + //_4 is for MASK + item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); + item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4)); + + // Children don't accept the focus + item->setTabStop(false); + + if (rearrange) + { + rearrangeItems(); + notifyParentItemsRectChanged(); + } + return true; +} + +bool LLFlatListView::addItemPairs(pairs_list_t panel_list, bool rearrange /*= true*/) +{ + if (!mItemComparator) + { + LL_WARNS_ONCE() << "No comparator specified for inserting FlatListView items." << LL_ENDL; + return false; + } + if (panel_list.size() == 0) + { + return false; + } + + // presort list so that it will be easier to sort elements into mItemPairs + panel_list.sort(ComparatorAdaptor(*mItemComparator)); + + pairs_const_iterator_t new_pair_it = panel_list.begin(); + item_pair_t* new_pair = *new_pair_it; + pairs_iterator_t pair_it = mItemPairs.begin(); + item_pair_t* item_pair = *pair_it; + + // sort panel_list into mItemPars + while (new_pair_it != panel_list.end() && pair_it != mItemPairs.end()) + { + if (!new_pair->first || new_pair->first->getParent() == mItemsPanel) + { + // iterator already used or we are reusing existing panel + new_pair_it++; + new_pair = *new_pair_it; + } + else if (mItemComparator->compare(new_pair->first, item_pair->first)) + { + LLPanel* panel = new_pair->first; + + mItemPairs.insert(pair_it, new_pair); + mItemsPanel->addChild(panel); + + //_4 is for MASK + panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); + panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4)); + // Children don't accept the focus + panel->setTabStop(false); + } + else + { + pair_it++; + item_pair = *pair_it; + } + } + + // Add what is left of panel_list into the end of mItemPairs. + for (; new_pair_it != panel_list.end(); ++new_pair_it) + { + item_pair_t* item_pair = *new_pair_it; + LLPanel *panel = item_pair->first; + if (panel && panel->getParent() != mItemsPanel) + { + mItemPairs.push_back(item_pair); + mItemsPanel->addChild(panel); + + //_4 is for MASK + panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, item_pair, _4)); + panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, item_pair, _4)); + // Children don't accept the focus + panel->setTabStop(false); + } + } + + if (rearrange) + { + rearrangeItems(); + notifyParentItemsRectChanged(); + } + return true; +} + + +bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/) +{ + if (!after_item) return false; + if (!item_to_add) return false; + if (value.isUndefined()) return false; + + if (mItemPairs.empty()) return false; + + //force uniqueness of items, easiest check but unreliable + if (item_to_add->getParent() == mItemsPanel) return false; + + item_pair_t* after_pair = getItemPair(after_item); + if (!after_pair) return false; + + item_pair_t* new_pair = new item_pair_t(item_to_add, value); + if (after_pair == mItemPairs.back()) + { + mItemPairs.push_back(new_pair); + mItemsPanel->addChild(item_to_add); + } + else + { + pairs_iterator_t it = mItemPairs.begin(); + for (; it != mItemPairs.end(); ++it) + { + if (*it == after_pair) + { + // insert new elements before the element at position of passed iterator. + mItemPairs.insert(++it, new_pair); + mItemsPanel->addChild(item_to_add); + break; + } + } + } + + //_4 is for MASK + item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); + item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4)); + + rearrangeItems(); + notifyParentItemsRectChanged(); + return true; +} + + +bool LLFlatListView::removeItem(LLPanel* item, bool rearrange) +{ + if (!item) return false; + if (item->getParent() != mItemsPanel) return false; + + item_pair_t* item_pair = getItemPair(item); + if (!item_pair) return false; + + return removeItemPair(item_pair, rearrange); +} + +bool LLFlatListView::removeItemByValue(const LLSD& value, bool rearrange) +{ + if (value.isUndefined()) return false; + + item_pair_t* item_pair = getItemPair(value); + if (!item_pair) return false; + + return removeItemPair(item_pair, rearrange); +} + +bool LLFlatListView::removeItemByUUID(const LLUUID& uuid, bool rearrange) +{ + return removeItemByValue(LLSD(uuid), rearrange); +} + +LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const +{ + if (value.isUndefined()) return NULL; + + item_pair_t* pair = getItemPair(value); + if (pair) return pair->first; + return NULL; +} + +bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/) +{ + if (!item) return false; + if (item->getParent() != mItemsPanel) return false; + + item_pair_t* item_pair = getItemPair(item); + if (!item_pair) return false; + + return selectItemPair(item_pair, select); +} + +bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/) +{ + if (value.isUndefined()) return false; + + item_pair_t* item_pair = getItemPair(value); + if (!item_pair) return false; + + return selectItemPair(item_pair, select); +} + +bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/) +{ + return selectItemByValue(LLSD(uuid), select); +} + + +LLSD LLFlatListView::getSelectedValue() const +{ + if (mSelectedItemPairs.empty()) return LLSD(); + + item_pair_t* first_selected_pair = mSelectedItemPairs.front(); + return first_selected_pair->second; +} + +void LLFlatListView::getSelectedValues(std::vector& selected_values) const +{ + if (mSelectedItemPairs.empty()) return; + + for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) + { + selected_values.push_back((*it)->second); + } +} + +LLUUID LLFlatListView::getSelectedUUID() const +{ + const LLSD& value = getSelectedValue(); + if (value.isDefined() && value.isUUID()) + { + return value.asUUID(); + } + else + { + return LLUUID::null; + } +} + +void LLFlatListView::getSelectedUUIDs(uuid_vec_t& selected_uuids) const +{ + if (mSelectedItemPairs.empty()) return; + + for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) + { + selected_uuids.push_back((*it)->second.asUUID()); + } +} + +LLPanel* LLFlatListView::getSelectedItem() const +{ + if (mSelectedItemPairs.empty()) return NULL; + + return mSelectedItemPairs.front()->first; +} + +void LLFlatListView::getSelectedItems(std::vector& selected_items) const +{ + if (mSelectedItemPairs.empty()) return; + + for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) + { + selected_items.push_back((*it)->first); + } +} + +void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/) +{ + if (mSelectedItemPairs.empty()) return; + + for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) + { + item_pair_t* pair_to_deselect = *it; + LLPanel* item = pair_to_deselect->first; + item->setValue(UNSELECTED_EVENT); + } + + mSelectedItemPairs.clear(); + + if (mCommitOnSelectionChange && !no_commit_on_deselection) + { + onCommit(); + } + + // Stretch selected item rect to ensure it won't be clipped + mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); +} + +void LLFlatListView::setNoItemsCommentText(const std::string& comment_text) +{ + mNoItemsCommentTextbox->setValue(comment_text); +} + +U32 LLFlatListView::size(const bool only_visible_items) const +{ + if (only_visible_items) + { + U32 size = 0; + for (pairs_const_iterator_t + iter = mItemPairs.begin(), + iter_end = mItemPairs.end(); + iter != iter_end; ++iter) + { + if ((*iter)->first->getVisible()) + ++size; + } + return size; + } + else + { + return mItemPairs.size(); + } +} + +void LLFlatListView::clear() +{ + // This will clear mSelectedItemPairs, calling all appropriate callbacks. + resetSelection(); + + // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex. + for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + mItemsPanel->removeChild((*it)->first); + (*it)->first->die(); + delete *it; + } + mItemPairs.clear(); + + // also set items panel height to zero. Reshape it to allow reshaping of non-item children + LLRect rc = mItemsPanel->getRect(); + rc.mBottom = rc.mTop; + mItemsPanel->reshape(rc.getWidth(), rc.getHeight()); + mItemsPanel->setRect(rc); + + setNoItemsCommentVisible(true); + notifyParentItemsRectChanged(); +} + +void LLFlatListView::sort() +{ + if (!mItemComparator) + { + LL_WARNS() << "No comparator specified for sorting FlatListView items." << LL_ENDL; + return; + } + + mItemPairs.sort(ComparatorAdaptor(*mItemComparator)); + rearrangeItems(); +} + +bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value) +{ + if (old_value.isUndefined() || new_value.isUndefined()) return false; + if (llsds_are_equal(old_value, new_value)) return false; + + item_pair_t* item_pair = getItemPair(old_value); + if (!item_pair) return false; + + item_pair->second = new_value; + return true; +} + +////////////////////////////////////////////////////////////////////////// +// PROTECTED STUFF +////////////////////////////////////////////////////////////////////////// + +LLFlatListView::LLFlatListView(const LLFlatListView::Params& p) +: LLScrollContainer(p) + , mItemComparator(NULL) + , mItemsPanel(NULL) + , mItemPad(p.item_pad) + , mAllowSelection(p.allow_select) + , mMultipleSelection(p.multi_select) + , mKeepOneItemSelected(p.keep_one_selected) + , mCommitOnSelectionChange(false) + , mPrevNotifyParentRect(LLRect()) + , mNoItemsCommentTextbox(NULL) + , mIsConsecutiveSelection(false) + , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape) +{ + mBorderThickness = getBorderWidth(); + + LLRect scroll_rect = getRect(); + LLRect items_rect; + + setItemsNoScrollWidth(scroll_rect.getWidth()); + items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0); + + LLPanel::Params pp; + pp.rect(items_rect); + mItemsPanel = LLUICtrlFactory::create (pp); + addChild(mItemsPanel); + + //we don't need to stretch in vertical direction on reshaping by a parent + //no bottom following! + mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP); + + LLViewBorder::Params params; + params.name("scroll border"); + params.rect(getLastSelectedItemRect()); + params.visible(false); + params.bevel_style(LLViewBorder::BEVEL_IN); + mSelectedItemsBorder = LLUICtrlFactory::create (params); + mItemsPanel->addChild( mSelectedItemsBorder ); + + { + // create textbox for "No Items" comment text + LLTextBox::Params text_p = p.no_items_text; + if (!text_p.rect.isProvided()) + { + LLRect comment_rect = getRect(); + comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight()); + comment_rect.stretch(-getBorderWidth()); + text_p.rect(comment_rect); + } + text_p.border_visible(false); + + if (!text_p.follows.isProvided()) + { + text_p.follows.flags(FOLLOWS_ALL); + } + mNoItemsCommentTextbox = LLUICtrlFactory::create(text_p, this); + } +}; + +LLFlatListView::~LLFlatListView() +{ + for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + mItemsPanel->removeChild((*it)->first); + (*it)->first->die(); + delete *it; + } + mItemPairs.clear(); +} + +// virtual +void LLFlatListView::draw() +{ + // Highlight border if a child of this container has keyboard focus + if( mSelectedItemsBorder->getVisible() ) + { + mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() ); + } + LLScrollContainer::draw(); +} + +// virtual +bool LLFlatListView::postBuild() +{ + setTabStop(true); + return LLScrollContainer::postBuild(); +} + +void LLFlatListView::rearrangeItems() +{ + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + setNoItemsCommentVisible(0==size()); + + if (mItemPairs.empty()) return; + + //calculating required height - assuming items can be of different height + //list should accommodate all its items + S32 height = 0; + + S32 invisible_children_count = 0; + pairs_iterator_t it = mItemPairs.begin(); + for (; it != mItemPairs.end(); ++it) + { + LLPanel* item = (*it)->first; + + // skip invisible child + if (!item->getVisible()) + { + ++invisible_children_count; + continue; + } + + height += item->getRect().getHeight(); + } + + // add paddings between items, excluding invisible ones + height += mItemPad * (mItemPairs.size() - invisible_children_count - 1); + + LLRect rc = mItemsPanel->getRect(); + S32 width = mItemsNoScrollWidth; + + // update width to avoid horizontal scrollbar + if (height > getRect().getHeight() - 2 * mBorderThickness) + width -= scrollbar_size; + + //changes the bottom, end of the list goes down in the scroll container + rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height); + mItemsPanel->setRect(rc); + + //reshaping items + S32 item_new_top = height; + pairs_iterator_t it2, first_it = mItemPairs.begin(); + for (it2 = first_it; it2 != mItemPairs.end(); ++it2) + { + LLPanel* item = (*it2)->first; + + // skip invisible child + if (!item->getVisible()) + continue; + + LLRect rc = item->getRect(); + rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight()); + item->reshape(rc.getWidth(), rc.getHeight()); + item->setRect(rc); + + // move top for next item in list + item_new_top -= (rc.getHeight() + mItemPad); + } + + // Stretch selected item rect to ensure it won't be clipped + mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); +} + +void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) +{ + if (!item_pair) return; + + if (!item_pair->first) + { + LL_WARNS() << "Attempt to selet an item pair containing null panel item" << LL_ENDL; + return; + } + + setFocus(true); + + bool select_item = !isSelected(item_pair); + + //*TODO find a better place for that enforcing stuff + if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return; + + if ( (mask & MASK_SHIFT) && !(mask & MASK_CONTROL) + && mMultipleSelection && !mSelectedItemPairs.empty() ) + { + item_pair_t* last_selected_pair = mSelectedItemPairs.back(); + + // If item_pair is already selected - do nothing + if (last_selected_pair == item_pair) + return; + + bool grab_items = false; + bool reverse = false; + pairs_list_t pairs_to_select; + + // Pick out items from list between last selected and current clicked item_pair. + for (pairs_iterator_t + iter = mItemPairs.begin(), + iter_end = mItemPairs.end(); + iter != iter_end; ++iter) + { + item_pair_t* cur = *iter; + if (cur == last_selected_pair || cur == item_pair) + { + // We've got reverse selection if last grabed item isn't a new selection. + reverse = grab_items && (cur != item_pair); + grab_items = !grab_items; + // Skip last selected and current clicked item pairs. + continue; + } + if (!cur->first->getVisible()) + { + // Skip invisible item pairs. + continue; + } + if (grab_items) + { + pairs_to_select.push_back(cur); + } + } + + if (reverse) + { + pairs_to_select.reverse(); + } + + pairs_to_select.push_back(item_pair); + + for (pairs_iterator_t + iter = pairs_to_select.begin(), + iter_end = pairs_to_select.end(); + iter != iter_end; ++iter) + { + item_pair_t* pair_to_select = *iter; + if (isSelected(pair_to_select)) + { + // Item was already selected but there is a need to keep order from last selected pair to new selection. + // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair(). + mSelectedItemPairs.remove(pair_to_select); + mSelectedItemPairs.push_back(pair_to_select); + } + else + { + selectItemPair(pair_to_select, true); + } + } + + if (!select_item) + { + // Update last selected item border. + mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); + } + return; + } + + //no need to do additional commit on selection reset + if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(true); + + //only CTRL usage allows to deselect an item, usual clicking on an item cannot deselect it + if (mask & MASK_CONTROL) + selectItemPair(item_pair, select_item); + else + selectItemPair(item_pair, true); +} + +void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask) +{ + if (!item_pair) + return; + + // Forbid deselecting of items on right mouse button click if mMultipleSelection flag is set on, + // because some of derived classes may have context menu and selected items must be kept. + if ( !(mask & MASK_CONTROL) && mMultipleSelection && isSelected(item_pair) ) + return; + + // else got same behavior as at onItemMouseClick + onItemMouseClick(item_pair, mask); +} + +bool LLFlatListView::handleKeyHere(KEY key, MASK mask) +{ + bool reset_selection = (mask != MASK_SHIFT); + bool handled = false; + switch (key) + { + case KEY_RETURN: + { + if (mSelectedItemPairs.size() && mask == MASK_NONE) + { + mOnReturnSignal(this, getValue()); + handled = true; + } + break; + } + case KEY_UP: + { + if ( !selectNextItemPair(true, reset_selection) && reset_selection) + { + // 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 + resetSelection(); + } + break; + } + case KEY_DOWN: + { + if ( !selectNextItemPair(false, reset_selection) && reset_selection) + { + // If case we are in accordion tab notify parent to go to the next accordion + if( notifyParent(LLSD().with("action","select_next")) > 0 ) //message was processed + resetSelection(); + } + break; + } + case KEY_ESCAPE: + { + if (mask == MASK_NONE) + { + setFocus(false); // pass focus to the game area (EXT-8357) + } + break; + } + default: + break; + } + + if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() ) + { + ensureSelectedVisible(); + /* + LLRect visible_rc = getVisibleContentRect(); + LLRect selected_rc = getLastSelectedItemRect(); + + if ( !visible_rc.contains (selected_rc) ) + { + // But scroll in Items panel coordinates + scrollToShowRect(selected_rc); + } + + // In case we are in accordion tab notify parent to show selected rectangle + LLRect screen_rc; + localRectToScreen(selected_rc, &screen_rc); + notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/ + + handled = true; + } + + return handled ? handled : LLScrollContainer::handleKeyHere(key, mask); +} + +LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const +{ + llassert(item); + + for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + item_pair_t* item_pair = *it; + if (item_pair->first == item) return item_pair; + } + return NULL; +} + +//compares two LLSD's +bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2) +{ + llassert(llsd_1.isDefined()); + llassert(llsd_2.isDefined()); + + if (llsd_1.type() != llsd_2.type()) return false; + + if (!llsd_1.isMap()) + { + if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID(); + + //assumptions that string representaion is enough for other types + return llsd_1.asString() == llsd_2.asString(); + } + + if (llsd_1.size() != llsd_2.size()) return false; + + LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap(); + LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap(); + for (S32 i = 0; i < llsd_1.size(); ++i) + { + if ((*llsd_1_it).first != (*llsd_2_it).first) return false; + if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false; + ++llsd_1_it; + ++llsd_2_it; + } + return true; +} + +LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const +{ + llassert(value.isDefined()); + + for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + item_pair_t* item_pair = *it; + if (llsds_are_equal(item_pair->second, value)) return item_pair; + } + return NULL; +} + +bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select) +{ + llassert(item_pair); + + if (!mAllowSelection && select) return false; + + if (isSelected(item_pair) == select) return true; //already in specified selection state + if (select) + { + mSelectedItemPairs.push_back(item_pair); + } + else + { + mSelectedItemPairs.remove(item_pair); + } + + //a way of notifying panel of selection state changes + LLPanel* item = item_pair->first; + item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT); + + if (mCommitOnSelectionChange) + { + onCommit(); + } + + // Stretch selected item rect to ensure it won't be clipped + mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); + // By default mark it as not consecutive selection + mIsConsecutiveSelection = false; + + return true; +} + +void LLFlatListView::scrollToShowFirstSelectedItem() +{ + if (!mSelectedItemPairs.size()) return; + + LLRect selected_rc = mSelectedItemPairs.front()->first->getRect(); + + if (selected_rc.isValid()) + { + scrollToShowRect(selected_rc); + } +} + +LLRect LLFlatListView::getLastSelectedItemRect() +{ + if (!mSelectedItemPairs.size()) + { + return LLRect::null; + } + + return mSelectedItemPairs.back()->first->getRect(); +} + +void LLFlatListView::selectFirstItem () +{ + // No items - no actions! + if (0 == size()) return; + + // Select first visible item + for (pairs_iterator_t + iter = mItemPairs.begin(), + iter_end = mItemPairs.end(); + iter != iter_end; ++iter) + { + // skip invisible items + if ( (*iter)->first->getVisible() ) + { + selectItemPair(*iter, true); + ensureSelectedVisible(); + break; + } + } +} + +void LLFlatListView::selectLastItem () +{ + // No items - no actions! + if (0 == size()) return; + + // Select last visible item + for (pairs_list_t::reverse_iterator + r_iter = mItemPairs.rbegin(), + r_iter_end = mItemPairs.rend(); + r_iter != r_iter_end; ++r_iter) + { + // skip invisible items + if ( (*r_iter)->first->getVisible() ) + { + selectItemPair(*r_iter, true); + ensureSelectedVisible(); + break; + } + } +} + +void LLFlatListView::ensureSelectedVisible() +{ + LLRect selected_rc = getLastSelectedItemRect(); + + if ( selected_rc.isValid() ) + { + scrollToShowRect(selected_rc); + } +} + + +// virtual +bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection) +{ + // No items - no actions! + if ( 0 == size() ) + return false; + + if (!mIsConsecutiveSelection) + { + // Leave only one item selected if list has not consecutive selection + if (mSelectedItemPairs.size() && !reset_selection) + { + item_pair_t* cur_sel_pair = mSelectedItemPairs.back(); + resetSelection(); + selectItemPair (cur_sel_pair, true); + } + } + + if ( mSelectedItemPairs.size() ) + { + item_pair_t* to_sel_pair = NULL; + item_pair_t* cur_sel_pair = NULL; + + // Take the last selected pair + cur_sel_pair = mSelectedItemPairs.back(); + // Bases on given direction choose next item to select + if ( is_up_direction ) + { + // Find current selected item position in mItemPairs list + pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair); + + for (;++sel_it != mItemPairs.rend();) + { + // skip invisible items + if ( (*sel_it)->first->getVisible() ) + { + to_sel_pair = *sel_it; + break; + } + } + } + else + { + // Find current selected item position in mItemPairs list + pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair); + + for (;++sel_it != mItemPairs.end();) + { + // skip invisible items + if ( (*sel_it)->first->getVisible() ) + { + to_sel_pair = *sel_it; + break; + } + } + } + + if ( to_sel_pair ) + { + bool select = true; + if ( reset_selection ) + { + // Reset current selection if we were asked about it + resetSelection(); + } + else + { + // If item already selected and no reset request than we should deselect last selected item. + select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair)); + } + // Select/Deselect next item + selectItemPair(select ? to_sel_pair : cur_sel_pair, select); + // Mark it as consecutive selection + mIsConsecutiveSelection = true; + return true; + } + } + else + { + // If there weren't selected items then choose the first one bases on given direction + // Force selection to first item + if (is_up_direction) + selectLastItem(); + else + selectFirstItem(); + // Mark it as consecutive selection + mIsConsecutiveSelection = true; + return true; + } + + return false; +} + +bool LLFlatListView::canSelectAll() const +{ + return 0 != size() && mAllowSelection && mMultipleSelection; +} + +void LLFlatListView::selectAll() +{ + if (!mAllowSelection || !mMultipleSelection) + return; + + mSelectedItemPairs.clear(); + + for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + item_pair_t* item_pair = *it; + mSelectedItemPairs.push_back(item_pair); + //a way of notifying panel of selection state changes + LLPanel* item = item_pair->first; + item->setValue(SELECTED_EVENT); + } + + if (mCommitOnSelectionChange) + { + onCommit(); + } + + // Stretch selected item rect to ensure it won't be clipped + mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); +} + +bool LLFlatListView::isSelected(item_pair_t* item_pair) const +{ + llassert(item_pair); + + pairs_const_iterator_t it_end = mSelectedItemPairs.end(); + return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end; +} + +bool LLFlatListView::removeItemPair(item_pair_t* item_pair, bool rearrange) +{ + llassert(item_pair); + + bool deleted = false; + bool selection_changed = false; + for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + item_pair_t* _item_pair = *it; + if (_item_pair == item_pair) + { + mItemPairs.erase(it); + deleted = true; + break; + } + } + + if (!deleted) return false; + + for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it) + { + item_pair_t* selected_item_pair = *it; + if (selected_item_pair == item_pair) + { + it = mSelectedItemPairs.erase(it); + selection_changed = true; + break; + } + } + + mItemsPanel->removeChild(item_pair->first); + item_pair->first->die(); + delete item_pair; + + if (rearrange) + { + rearrangeItems(); + notifyParentItemsRectChanged(); + } + + if (selection_changed && mCommitOnSelectionChange) + { + onCommit(); + } + + return true; +} + +void LLFlatListView::notifyParentItemsRectChanged() +{ + S32 comment_height = 0; + + // take into account comment text height if exists + if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible()) + { + // top text padding inside the textbox is included into the height + comment_height = mNoItemsCommentTextbox->getTextPixelHeight(); + + // take into account a distance from parent's top border to textbox's top + comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop; + } + + LLRect req_rect = getItemsRect(); + + // get maximum of items total height and comment text height + req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height)); + + // take into account border size. + req_rect.stretch(getBorderWidth()); + + if (req_rect == mPrevNotifyParentRect) + return; + + mPrevNotifyParentRect = req_rect; + + LLSD params; + params["action"] = "size_changes"; + params["width"] = req_rect.getWidth(); + params["height"] = req_rect.getHeight(); + + if (getParent()) // dummy widgets don't have a parent + getParent()->notifyParent(params); +} + +void LLFlatListView::setNoItemsCommentVisible(bool visible) const +{ + if (mNoItemsCommentTextbox) + { + mSelectedItemsBorder->setVisible(!visible); + mNoItemsCommentTextbox->setVisible(visible); + } +} + +void LLFlatListView::getItems(std::vector& items) const +{ + if (mItemPairs.empty()) return; + + items.clear(); + for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + items.push_back((*it)->first); + } +} + +void LLFlatListView::getValues(std::vector& values) const +{ + if (mItemPairs.empty()) return; + + values.clear(); + for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + values.push_back((*it)->second); + } +} + +// virtual +void LLFlatListView::onFocusReceived() +{ + if (size()) + { + mSelectedItemsBorder->setVisible(true); + } + gEditMenuHandler = this; +} +// virtual +void LLFlatListView::onFocusLost() +{ + mSelectedItemsBorder->setVisible(false); + // Route menu back to the default + if (gEditMenuHandler == this) + { + gEditMenuHandler = NULL; + } +} + +//virtual +S32 LLFlatListView::notify(const LLSD& info) +{ + if (info.has("action")) + { + std::string str_action = info["action"]; + if (str_action == "select_first") + { + setFocus(true); + selectFirstItem(); + return 1; + } + else if (str_action == "select_last") + { + setFocus(true); + selectLastItem(); + return 1; + } + } + else if (info.has("rearrange")) + { + rearrangeItems(); + notifyParentItemsRectChanged(); + return 1; + } + + return 0; +} + +void LLFlatListView::detachItems(std::vector& detached_items) +{ + LLSD action; + action.with("detach", LLSD()); + // Clear detached_items list + detached_items.clear(); + // Go through items and detach valid items, remove them from items panel + // and add to detached_items. + pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); + while (iter != iter_end) + { + LLPanel* pItem = (*iter)->first; + if (1 == pItem->notify(action)) + { + selectItemPair((*iter), false); + mItemsPanel->removeChild(pItem); + detached_items.push_back(pItem); + } + iter++; + } + if (!detached_items.empty()) + { + // Some items were detached, clean ourself from unusable memory + if (detached_items.size() == mItemPairs.size()) + { + // This way will be faster if all items were disconnected + pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); + while (iter != iter_end) + { + (*iter)->first = NULL; + delete *iter; + iter++; + } + mItemPairs.clear(); + // Also set items panel height to zero. + // Reshape it to allow reshaping of non-item children. + LLRect rc = mItemsPanel->getRect(); + rc.mBottom = rc.mTop; + mItemsPanel->reshape(rc.getWidth(), rc.getHeight()); + mItemsPanel->setRect(rc); + setNoItemsCommentVisible(true); + } + else + { + std::vector::const_iterator + detached_iter = detached_items.begin(), + detached_iter_end = detached_items.end(); + while (detached_iter < detached_iter_end) + { + LLPanel* pDetachedItem = *detached_iter; + pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); + while (iter != iter_end) + { + item_pair_t* item_pair = *iter; + if (item_pair->first == pDetachedItem) + { + mItemPairs.erase(iter); + item_pair->first = NULL; + delete item_pair; + break; + } + iter++; + } + detached_iter++; + } + rearrangeItems(); + } + notifyParentItemsRectChanged(); + } +} + + +/************************************************************************/ +/* LLFlatListViewEx implementation */ +/************************************************************************/ +LLFlatListViewEx::Params::Params() +: no_items_msg("no_items_msg") +, no_filtered_items_msg("no_filtered_items_msg") +{ +} + +LLFlatListViewEx::LLFlatListViewEx(const Params& p) +: LLFlatListView(p) +, mNoFilteredItemsMsg(p.no_filtered_items_msg) +, mNoItemsMsg(p.no_items_msg) +, mForceShowingUnmatchedItems(false) +, mHasMatchedItems(false) +{ +} + +void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string) +{ + bool items_filtered = !filter_string.empty(); + if (items_filtered) + { + // items were filtered + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(filter_string); + std::string text = mNoFilteredItemsMsg; + LLStringUtil::format(text, args); + setNoItemsCommentText(text); + } + else + { + // list does not contain any items at all + setNoItemsCommentText(mNoItemsMsg); + } +} + +bool LLFlatListViewEx::getForceShowingUnmatchedItems() +{ + return mForceShowingUnmatchedItems; +} + +void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show) +{ + mForceShowingUnmatchedItems = show; +} + +void LLFlatListViewEx::setFilterSubString(const std::string& filter_str, bool notify_parent) +{ + if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString)) + { + mFilterSubString = filter_str; + updateNoItemsMessage(mFilterSubString); + filterItems(false, notify_parent); + } +} + +bool LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action) +{ + if (!item) + return false; + + bool visible = true; + + // 0 signifies that filter is matched, + // i.e. we don't hide items that don't support 'match_filter' action, separators etc. + if (0 == item->notify(action)) + { + mHasMatchedItems = true; + } + else + { + // TODO: implement (re)storing of current selection. + if (!mForceShowingUnmatchedItems) + { + selectItem(item, false); + visible = false; + } + } + + if (item->getVisible() != visible) + { + item->setVisible(visible); + return true; + } + + return false; +} + +void LLFlatListViewEx::filterItems(bool re_sort, bool notify_parent) +{ + std::string cur_filter = mFilterSubString; + LLStringUtil::toUpper(cur_filter); + + LLSD action; + action.with("match_filter", cur_filter); + + mHasMatchedItems = false; + bool visibility_changed = false; + pairs_const_iterator_t iter = getItemPairs().begin(), iter_end = getItemPairs().end(); + while (iter != iter_end) + { + LLPanel* pItem = (*(iter++))->first; + visibility_changed |= updateItemVisibility(pItem, action); + } + + if (re_sort) + { + sort(); + } + + if (visibility_changed && notify_parent) + { + notifyParentItemsRectChanged(); + } +} + +bool LLFlatListViewEx::hasMatchedItems() +{ + return mHasMatchedItems; +} + +//EOF diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index c24fd34ae6..b64a9862a2 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -1,535 +1,535 @@ -/** - * @file llflatlistview.h - * @brief LLFlatListView base class and extension to support messages for several cases of an empty list. - * - * $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_LLFLATLISTVIEW_H -#define LL_LLFLATLISTVIEW_H - -#include "llpanel.h" -#include "llscrollcontainer.h" -#include "lltextbox.h" - - -/** - * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's. - * LLSD can be associated with each added item, it can keep data from an item in digested form. - * Associated LLSD's can be of any type (singular, a map etc.). - * Items (LLPanel's subclasses) can be of different height. - * The list is LLPanel created in itself and grows in height while new items are added. - * - * The control can manage selection of its items when the flag "allow_select" is set. Also ability to select - * multiple items (by using CTRL) is enabled through setting the flag "multi_select" - if selection is not allowed that flag - * is ignored. The option "keep_one_selected" forces at least one item to be selected at any time (only for mouse events on items) - * since any item of the list was selected. - * - * Examples of using this control are presented in Picks panel (My Profile and Profile View), where this control is used to - * manage the list of pick items. - * - * ASSUMPTIONS AND STUFF - * - NULL pointers and undefined LLSD's are not accepted by any method of this class unless specified otherwise - * - Order of returned selected items are not guaranteed - * - The control assumes that all items being added are unique. - */ -class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler -{ - LOG_CLASS(LLFlatListView); -public: - - /** - * Abstract comparator for comparing flat list items in a form of LLPanel - */ - class ItemComparator - { - public: - ItemComparator() {}; - virtual ~ItemComparator() {}; - - /** Returns true if item1 < item2, false otherwise */ - virtual bool compare(const LLPanel* item1, const LLPanel* item2) const = 0; - }; - - /** - * Represents reverse comparator which acts as a decorator for a comparator that need to be reversed - */ - class ItemReverseComparator : public ItemComparator - { - public: - ItemReverseComparator(const ItemComparator& comparator) : mComparator(comparator) {}; - virtual ~ItemReverseComparator() {}; - - virtual bool compare(const LLPanel* item1, const LLPanel* item2) const - { - return mComparator.compare(item2, item1); - } - - private: - const ItemComparator& mComparator; - }; - - - struct Params : public LLInitParam::Block - { - /** turning on/off selection support */ - Optional allow_select; - - /** turning on/off multiple selection (works while clicking and holding CTRL)*/ - Optional multi_select; - - /** don't allow to deselect all selected items (for mouse events on items only) */ - Optional keep_one_selected; - - /** try to keep selection visible after reshape */ - Optional keep_selection_visible_on_reshape; - - /** padding between items */ - Optional item_pad; - - /** textbox with info message when list is empty*/ - Optional no_items_text; - - Params(); - }; - - // disable traversal when finding widget to hand focus off to - /*virtual*/ bool canFocusChildren() const { return false; } - - /** - * Connects callback to signal called when Return key is pressed. - */ - boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); } - - /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */ - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - - /** Returns full rect of child panel */ - const LLRect& getItemsRect() const; - - LLRect getRequiredRect() { return getItemsRect(); } - - /** Returns distance between items */ - const S32 getItemsPad() { return mItemPad; } - - /** - * Adds and item and LLSD value associated with it to the list at specified position - * @return true if the item was added, false otherwise - */ - virtual bool addItem(LLPanel * item, const LLSD& value = LLUUID::null, EAddPosition pos = ADD_BOTTOM, bool rearrange = true); - - /** - * Insert item_to_add along with associated value to the list right after the after_item. - * @return true if the item was successfully added, false otherwise - */ - virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value = LLUUID::null); - - /** - * Remove specified item - * @return true if the item was removed, false otherwise - */ - virtual bool removeItem(LLPanel* item, bool rearrange = true); - - /** - * Remove an item specified by value - * @return true if the item was removed, false otherwise - */ - virtual bool removeItemByValue(const LLSD& value, bool rearrange = true); - - /** - * Remove an item specified by uuid - * @return true if the item was removed, false otherwise - */ - virtual bool removeItemByUUID(const LLUUID& uuid, bool rearrange = true); - - /** - * Get an item by value - * @return the item as LLPanel if associated with value, NULL otherwise - */ - virtual LLPanel* getItemByValue(const LLSD& value) const; - - template - T* getTypedItemByValue(const LLSD& value) const - { - return dynamic_cast(getItemByValue(value)); - } - - /** - * Select or deselect specified item based on select - * @return true if succeed, false otherwise - */ - virtual bool selectItem(LLPanel* item, bool select = true); - - /** - * Select or deselect an item by associated value based on select - * @return true if succeed, false otherwise - */ - virtual bool selectItemByValue(const LLSD& value, bool select = true); - - /** - * Select or deselect an item by associated uuid based on select - * @return true if succeed, false otherwise - */ - virtual bool selectItemByUUID(const LLUUID& uuid, bool select = true); - - /** - * Get all panels stored in the list. - */ - virtual void getItems(std::vector& items) const; - - /** - * Get all items values. - */ - virtual void getValues(std::vector& values) const; - - /** - * Get LLSD associated with the first selected item - */ - virtual LLSD getSelectedValue() const; - - /** - * Get LLSD's associated with selected items. - * @param selected_values std::vector being populated with LLSD associated with selected items - */ - virtual void getSelectedValues(std::vector& selected_values) const; - - - /** - * Get LLUUID associated with selected item - * @return LLUUID if such was associated with selected item - */ - virtual LLUUID getSelectedUUID() const; - - /** - * Get LLUUIDs associated with selected items - * @param selected_uuids An std::vector being populated with LLUUIDs associated with selected items - */ - virtual void getSelectedUUIDs(uuid_vec_t& selected_uuids) const; - - /** Get the top selected item */ - virtual LLPanel* getSelectedItem() const; - - /** - * Get selected items - * @param selected_items An std::vector being populated with pointers to selected items - */ - virtual void getSelectedItems(std::vector& selected_items) const; - - - /** - * Resets selection of items. - * - * It calls onCommit callback if setCommitOnSelectionChange(bool b) was called with "true" - * argument for current Flat List. - * @param no_commit_on_deselection - if true onCommit callback will not be called - */ - virtual void resetSelection(bool no_commit_on_deselection = false); - - /** - * Sets comment text which will be shown in the list is it is empty. - * - * Textbox to hold passed text is created while this method is called at the first time. - * - * @param comment_text - string to be shown as a comment. - */ - void setNoItemsCommentText( const std::string& comment_text); - - /** Turn on/off multiple selection support */ - void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; } - - /** Turn on/off selection support */ - void setAllowSelection(bool can_select) { mAllowSelection = can_select; } - - /** Sets flag whether onCommit should be fired if selection was changed */ - // FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly. - void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; } - - /** Get number of selected items in the list */ - U32 numSelected() const {return mSelectedItemPairs.size(); } - - /** Get number of (visible) items in the list */ - U32 size(const bool only_visible_items = true) const; - - /** Removes all items from the list */ - virtual void clear(); - - /** - * Removes all items that can be detached from the list but doesn't destroy - * them, caller responsible to manage items after they are detached. - * Detachable item should accept "detach" action via notify() method, - * where it disconnect all callbacks, does other valuable routines and - * return 1. - */ - void detachItems(std::vector& detached_items); - - /** - * Set comparator to use for future sorts. - * - * This class does NOT manage lifetime of the comparator - * but assumes that the comparator is always alive. - */ - void setComparator(const ItemComparator* comp) { mItemComparator = comp; } - void sort(); - - bool updateValue(const LLSD& old_value, const LLSD& new_value); - - void scrollToShowFirstSelectedItem(); - - void selectFirstItem (); - void selectLastItem (); - - virtual S32 notify(const LLSD& info) ; - - virtual ~LLFlatListView(); - -protected: - - /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */ - typedef std::pair item_pair_t; - - typedef std::list pairs_list_t; - typedef pairs_list_t::iterator pairs_iterator_t; - typedef pairs_list_t::const_iterator pairs_const_iterator_t; - - /** An adapter for a ItemComparator */ - struct ComparatorAdaptor - { - ComparatorAdaptor(const ItemComparator& comparator) : mComparator(comparator) {}; - - bool operator()(const item_pair_t* item_pair1, const item_pair_t* item_pair2) - { - return mComparator.compare(item_pair1->first, item_pair2->first); - } - - const ItemComparator& mComparator; - }; - - - friend class LLUICtrlFactory; - LLFlatListView(const LLFlatListView::Params& p); - - /** Manage selection on mouse events */ - void onItemMouseClick(item_pair_t* item_pair, MASK mask); - - void onItemRightMouseClick(item_pair_t* item_pair, MASK mask); - - /** - * Updates position of items. - * It does not take into account invisible items. - */ - virtual void rearrangeItems(); - - virtual item_pair_t* getItemPair(LLPanel* item) const; - - virtual item_pair_t* getItemPair(const LLSD& value) const; - - virtual bool selectItemPair(item_pair_t* item_pair, bool select); - - virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection); - - virtual bool canSelectAll() const; - virtual void selectAll(); - - virtual bool isSelected(item_pair_t* item_pair) const; - - virtual bool removeItemPair(item_pair_t* item_pair, bool rearrange); - - bool addItemPairs(pairs_list_t panel_list, bool rearrange = true); - - /** - * Notify parent about changed size of internal controls with "size_changes" action - * - * Size includes Items Rect width and either Items Rect height or comment text height. - * Comment text height is included if comment text is set and visible. - * List border size is also included into notified size. - */ - void notifyParentItemsRectChanged(); - - virtual bool handleKeyHere(KEY key, MASK mask); - - virtual bool postBuild(); - - virtual void onFocusReceived(); - - virtual void onFocusLost(); - - virtual void draw(); - - LLRect getLastSelectedItemRect(); - - void ensureSelectedVisible(); - - const pairs_list_t& getItemPairs() { return mItemPairs; } - -private: - - void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;} - - void setNoItemsCommentVisible(bool visible) const; - -protected: - - /** Comparator to use when sorting the list. */ - const ItemComparator* mItemComparator; - - -private: - - LLPanel* mItemsPanel; - - S32 mItemsNoScrollWidth; - - S32 mBorderThickness; - - /** Items padding */ - S32 mItemPad; - - /** Selection support flag */ - bool mAllowSelection; - - /** Multiselection support flag, ignored if selection is not supported */ - bool mMultipleSelection; - - /** - * Flag specified whether onCommit be called if selection is changed in the list. - * - * Can be ignored in the resetSelection() method. - * @see resetSelection() - */ - bool mCommitOnSelectionChange; - - bool mKeepOneItemSelected; - - bool mIsConsecutiveSelection; - - bool mKeepSelectionVisibleOnReshape; - - /** All pairs of the list */ - pairs_list_t mItemPairs; - - /** Selected pairs for faster access */ - pairs_list_t mSelectedItemPairs; - - /** - * Rectangle contained previous size of items parent notified last time. - * Is used to reduce amount of parentNotify() calls if size was not changed. - */ - LLRect mPrevNotifyParentRect; - - LLTextBox* mNoItemsCommentTextbox; - - LLViewBorder* mSelectedItemsBorder; - - commit_signal_t mOnReturnSignal; -}; - -/** - * Extends LLFlatListView functionality to show different messages when there are no items in the - * list depend on whether they are filtered or not. - * - * Class provides one message per case of empty list. - * It also provides protected updateNoItemsMessage() method to be called each time when derived list - * is changed to update base mNoItemsCommentTextbox value. - * - * It is implemented to avoid duplication of this functionality in concrete implementations of the - * lists. It is intended to be used as a base class for lists which should support two different - * messages for empty state. Can be improved to support more than two messages via state-to-message map. - */ -class LLFlatListViewEx : public LLFlatListView -{ -public: - LOG_CLASS(LLFlatListViewEx); - - struct Params : public LLInitParam::Block - { - /** - * Contains a message for empty list when it does not contain any items at all. - */ - Optional no_items_msg; - - /** - * Contains a message for empty list when its items are removed by filtering. - */ - Optional no_filtered_items_msg; - Params(); - }; - - // *WORKAROUND: two methods to overload appropriate Params due to localization issue: - // no_items_msg & no_filtered_items_msg attributes are not defined as translatable in VLT. See EXT-5931 - void setNoItemsMsg(const std::string& msg) { mNoItemsMsg = msg; } - void setNoFilteredItemsMsg(const std::string& msg) { mNoFilteredItemsMsg = msg; } - - bool getForceShowingUnmatchedItems(); - - void setForceShowingUnmatchedItems(bool show); - - /** - * Sets up new filter string and filters the list. - */ - void setFilterSubString(const std::string& filter_str, bool notify_parent); - std::string getFilterSubString() { return mFilterSubString; } - - /** - * Filters the list, rearranges and notifies parent about shape changes. - * Derived classes may want to overload rearrangeItems() to exclude repeated separators after filtration. - */ - void filterItems(bool re_sort, bool notify_parent); - - /** - * Returns true if last call of filterItems() found at least one matching item - */ - bool hasMatchedItems(); - -protected: - LLFlatListViewEx(const Params& p); - - /** - * Applies a message for empty list depend on passed argument. - * - * @param filter_string - if is not empty, message for filtered items will be set, otherwise for - * completely empty list. Value of filter string will be passed as search_term in SLURL. - */ - void updateNoItemsMessage(const std::string& filter_string); - - /** - * Applies visibility acording to action and LLFlatListView settings. - * - * @param item - item we are changing - * @param item - action - parameters to determin visibility from - */ - bool updateItemVisibility(LLPanel* item, const LLSD &action); - -private: - std::string mNoFilteredItemsMsg; - std::string mNoItemsMsg; - std::string mFilterSubString; - /** - * Show list items that don't match current filter - */ - bool mForceShowingUnmatchedItems; - /** - * True if last call of filterItems() found at least one matching item - */ - bool mHasMatchedItems; -}; - -#endif +/** + * @file llflatlistview.h + * @brief LLFlatListView base class and extension to support messages for several cases of an empty list. + * + * $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_LLFLATLISTVIEW_H +#define LL_LLFLATLISTVIEW_H + +#include "llpanel.h" +#include "llscrollcontainer.h" +#include "lltextbox.h" + + +/** + * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's. + * LLSD can be associated with each added item, it can keep data from an item in digested form. + * Associated LLSD's can be of any type (singular, a map etc.). + * Items (LLPanel's subclasses) can be of different height. + * The list is LLPanel created in itself and grows in height while new items are added. + * + * The control can manage selection of its items when the flag "allow_select" is set. Also ability to select + * multiple items (by using CTRL) is enabled through setting the flag "multi_select" - if selection is not allowed that flag + * is ignored. The option "keep_one_selected" forces at least one item to be selected at any time (only for mouse events on items) + * since any item of the list was selected. + * + * Examples of using this control are presented in Picks panel (My Profile and Profile View), where this control is used to + * manage the list of pick items. + * + * ASSUMPTIONS AND STUFF + * - NULL pointers and undefined LLSD's are not accepted by any method of this class unless specified otherwise + * - Order of returned selected items are not guaranteed + * - The control assumes that all items being added are unique. + */ +class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler +{ + LOG_CLASS(LLFlatListView); +public: + + /** + * Abstract comparator for comparing flat list items in a form of LLPanel + */ + class ItemComparator + { + public: + ItemComparator() {}; + virtual ~ItemComparator() {}; + + /** Returns true if item1 < item2, false otherwise */ + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const = 0; + }; + + /** + * Represents reverse comparator which acts as a decorator for a comparator that need to be reversed + */ + class ItemReverseComparator : public ItemComparator + { + public: + ItemReverseComparator(const ItemComparator& comparator) : mComparator(comparator) {}; + virtual ~ItemReverseComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const + { + return mComparator.compare(item2, item1); + } + + private: + const ItemComparator& mComparator; + }; + + + struct Params : public LLInitParam::Block + { + /** turning on/off selection support */ + Optional allow_select; + + /** turning on/off multiple selection (works while clicking and holding CTRL)*/ + Optional multi_select; + + /** don't allow to deselect all selected items (for mouse events on items only) */ + Optional keep_one_selected; + + /** try to keep selection visible after reshape */ + Optional keep_selection_visible_on_reshape; + + /** padding between items */ + Optional item_pad; + + /** textbox with info message when list is empty*/ + Optional no_items_text; + + Params(); + }; + + // disable traversal when finding widget to hand focus off to + /*virtual*/ bool canFocusChildren() const { return false; } + + /** + * Connects callback to signal called when Return key is pressed. + */ + boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); } + + /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */ + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + + /** Returns full rect of child panel */ + const LLRect& getItemsRect() const; + + LLRect getRequiredRect() { return getItemsRect(); } + + /** Returns distance between items */ + const S32 getItemsPad() { return mItemPad; } + + /** + * Adds and item and LLSD value associated with it to the list at specified position + * @return true if the item was added, false otherwise + */ + virtual bool addItem(LLPanel * item, const LLSD& value = LLUUID::null, EAddPosition pos = ADD_BOTTOM, bool rearrange = true); + + /** + * Insert item_to_add along with associated value to the list right after the after_item. + * @return true if the item was successfully added, false otherwise + */ + virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value = LLUUID::null); + + /** + * Remove specified item + * @return true if the item was removed, false otherwise + */ + virtual bool removeItem(LLPanel* item, bool rearrange = true); + + /** + * Remove an item specified by value + * @return true if the item was removed, false otherwise + */ + virtual bool removeItemByValue(const LLSD& value, bool rearrange = true); + + /** + * Remove an item specified by uuid + * @return true if the item was removed, false otherwise + */ + virtual bool removeItemByUUID(const LLUUID& uuid, bool rearrange = true); + + /** + * Get an item by value + * @return the item as LLPanel if associated with value, NULL otherwise + */ + virtual LLPanel* getItemByValue(const LLSD& value) const; + + template + T* getTypedItemByValue(const LLSD& value) const + { + return dynamic_cast(getItemByValue(value)); + } + + /** + * Select or deselect specified item based on select + * @return true if succeed, false otherwise + */ + virtual bool selectItem(LLPanel* item, bool select = true); + + /** + * Select or deselect an item by associated value based on select + * @return true if succeed, false otherwise + */ + virtual bool selectItemByValue(const LLSD& value, bool select = true); + + /** + * Select or deselect an item by associated uuid based on select + * @return true if succeed, false otherwise + */ + virtual bool selectItemByUUID(const LLUUID& uuid, bool select = true); + + /** + * Get all panels stored in the list. + */ + virtual void getItems(std::vector& items) const; + + /** + * Get all items values. + */ + virtual void getValues(std::vector& values) const; + + /** + * Get LLSD associated with the first selected item + */ + virtual LLSD getSelectedValue() const; + + /** + * Get LLSD's associated with selected items. + * @param selected_values std::vector being populated with LLSD associated with selected items + */ + virtual void getSelectedValues(std::vector& selected_values) const; + + + /** + * Get LLUUID associated with selected item + * @return LLUUID if such was associated with selected item + */ + virtual LLUUID getSelectedUUID() const; + + /** + * Get LLUUIDs associated with selected items + * @param selected_uuids An std::vector being populated with LLUUIDs associated with selected items + */ + virtual void getSelectedUUIDs(uuid_vec_t& selected_uuids) const; + + /** Get the top selected item */ + virtual LLPanel* getSelectedItem() const; + + /** + * Get selected items + * @param selected_items An std::vector being populated with pointers to selected items + */ + virtual void getSelectedItems(std::vector& selected_items) const; + + + /** + * Resets selection of items. + * + * It calls onCommit callback if setCommitOnSelectionChange(bool b) was called with "true" + * argument for current Flat List. + * @param no_commit_on_deselection - if true onCommit callback will not be called + */ + virtual void resetSelection(bool no_commit_on_deselection = false); + + /** + * Sets comment text which will be shown in the list is it is empty. + * + * Textbox to hold passed text is created while this method is called at the first time. + * + * @param comment_text - string to be shown as a comment. + */ + void setNoItemsCommentText( const std::string& comment_text); + + /** Turn on/off multiple selection support */ + void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; } + + /** Turn on/off selection support */ + void setAllowSelection(bool can_select) { mAllowSelection = can_select; } + + /** Sets flag whether onCommit should be fired if selection was changed */ + // FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly. + void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; } + + /** Get number of selected items in the list */ + U32 numSelected() const {return mSelectedItemPairs.size(); } + + /** Get number of (visible) items in the list */ + U32 size(const bool only_visible_items = true) const; + + /** Removes all items from the list */ + virtual void clear(); + + /** + * Removes all items that can be detached from the list but doesn't destroy + * them, caller responsible to manage items after they are detached. + * Detachable item should accept "detach" action via notify() method, + * where it disconnect all callbacks, does other valuable routines and + * return 1. + */ + void detachItems(std::vector& detached_items); + + /** + * Set comparator to use for future sorts. + * + * This class does NOT manage lifetime of the comparator + * but assumes that the comparator is always alive. + */ + void setComparator(const ItemComparator* comp) { mItemComparator = comp; } + void sort(); + + bool updateValue(const LLSD& old_value, const LLSD& new_value); + + void scrollToShowFirstSelectedItem(); + + void selectFirstItem (); + void selectLastItem (); + + virtual S32 notify(const LLSD& info) ; + + virtual ~LLFlatListView(); + +protected: + + /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */ + typedef std::pair item_pair_t; + + typedef std::list pairs_list_t; + typedef pairs_list_t::iterator pairs_iterator_t; + typedef pairs_list_t::const_iterator pairs_const_iterator_t; + + /** An adapter for a ItemComparator */ + struct ComparatorAdaptor + { + ComparatorAdaptor(const ItemComparator& comparator) : mComparator(comparator) {}; + + bool operator()(const item_pair_t* item_pair1, const item_pair_t* item_pair2) + { + return mComparator.compare(item_pair1->first, item_pair2->first); + } + + const ItemComparator& mComparator; + }; + + + friend class LLUICtrlFactory; + LLFlatListView(const LLFlatListView::Params& p); + + /** Manage selection on mouse events */ + void onItemMouseClick(item_pair_t* item_pair, MASK mask); + + void onItemRightMouseClick(item_pair_t* item_pair, MASK mask); + + /** + * Updates position of items. + * It does not take into account invisible items. + */ + virtual void rearrangeItems(); + + virtual item_pair_t* getItemPair(LLPanel* item) const; + + virtual item_pair_t* getItemPair(const LLSD& value) const; + + virtual bool selectItemPair(item_pair_t* item_pair, bool select); + + virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection); + + virtual bool canSelectAll() const; + virtual void selectAll(); + + virtual bool isSelected(item_pair_t* item_pair) const; + + virtual bool removeItemPair(item_pair_t* item_pair, bool rearrange); + + bool addItemPairs(pairs_list_t panel_list, bool rearrange = true); + + /** + * Notify parent about changed size of internal controls with "size_changes" action + * + * Size includes Items Rect width and either Items Rect height or comment text height. + * Comment text height is included if comment text is set and visible. + * List border size is also included into notified size. + */ + void notifyParentItemsRectChanged(); + + virtual bool handleKeyHere(KEY key, MASK mask); + + virtual bool postBuild(); + + virtual void onFocusReceived(); + + virtual void onFocusLost(); + + virtual void draw(); + + LLRect getLastSelectedItemRect(); + + void ensureSelectedVisible(); + + const pairs_list_t& getItemPairs() { return mItemPairs; } + +private: + + void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;} + + void setNoItemsCommentVisible(bool visible) const; + +protected: + + /** Comparator to use when sorting the list. */ + const ItemComparator* mItemComparator; + + +private: + + LLPanel* mItemsPanel; + + S32 mItemsNoScrollWidth; + + S32 mBorderThickness; + + /** Items padding */ + S32 mItemPad; + + /** Selection support flag */ + bool mAllowSelection; + + /** Multiselection support flag, ignored if selection is not supported */ + bool mMultipleSelection; + + /** + * Flag specified whether onCommit be called if selection is changed in the list. + * + * Can be ignored in the resetSelection() method. + * @see resetSelection() + */ + bool mCommitOnSelectionChange; + + bool mKeepOneItemSelected; + + bool mIsConsecutiveSelection; + + bool mKeepSelectionVisibleOnReshape; + + /** All pairs of the list */ + pairs_list_t mItemPairs; + + /** Selected pairs for faster access */ + pairs_list_t mSelectedItemPairs; + + /** + * Rectangle contained previous size of items parent notified last time. + * Is used to reduce amount of parentNotify() calls if size was not changed. + */ + LLRect mPrevNotifyParentRect; + + LLTextBox* mNoItemsCommentTextbox; + + LLViewBorder* mSelectedItemsBorder; + + commit_signal_t mOnReturnSignal; +}; + +/** + * Extends LLFlatListView functionality to show different messages when there are no items in the + * list depend on whether they are filtered or not. + * + * Class provides one message per case of empty list. + * It also provides protected updateNoItemsMessage() method to be called each time when derived list + * is changed to update base mNoItemsCommentTextbox value. + * + * It is implemented to avoid duplication of this functionality in concrete implementations of the + * lists. It is intended to be used as a base class for lists which should support two different + * messages for empty state. Can be improved to support more than two messages via state-to-message map. + */ +class LLFlatListViewEx : public LLFlatListView +{ +public: + LOG_CLASS(LLFlatListViewEx); + + struct Params : public LLInitParam::Block + { + /** + * Contains a message for empty list when it does not contain any items at all. + */ + Optional no_items_msg; + + /** + * Contains a message for empty list when its items are removed by filtering. + */ + Optional no_filtered_items_msg; + Params(); + }; + + // *WORKAROUND: two methods to overload appropriate Params due to localization issue: + // no_items_msg & no_filtered_items_msg attributes are not defined as translatable in VLT. See EXT-5931 + void setNoItemsMsg(const std::string& msg) { mNoItemsMsg = msg; } + void setNoFilteredItemsMsg(const std::string& msg) { mNoFilteredItemsMsg = msg; } + + bool getForceShowingUnmatchedItems(); + + void setForceShowingUnmatchedItems(bool show); + + /** + * Sets up new filter string and filters the list. + */ + void setFilterSubString(const std::string& filter_str, bool notify_parent); + std::string getFilterSubString() { return mFilterSubString; } + + /** + * Filters the list, rearranges and notifies parent about shape changes. + * Derived classes may want to overload rearrangeItems() to exclude repeated separators after filtration. + */ + void filterItems(bool re_sort, bool notify_parent); + + /** + * Returns true if last call of filterItems() found at least one matching item + */ + bool hasMatchedItems(); + +protected: + LLFlatListViewEx(const Params& p); + + /** + * Applies a message for empty list depend on passed argument. + * + * @param filter_string - if is not empty, message for filtered items will be set, otherwise for + * completely empty list. Value of filter string will be passed as search_term in SLURL. + */ + void updateNoItemsMessage(const std::string& filter_string); + + /** + * Applies visibility acording to action and LLFlatListView settings. + * + * @param item - item we are changing + * @param item - action - parameters to determin visibility from + */ + bool updateItemVisibility(LLPanel* item, const LLSD &action); + +private: + std::string mNoFilteredItemsMsg; + std::string mNoItemsMsg; + std::string mFilterSubString; + /** + * Show list items that don't match current filter + */ + bool mForceShowingUnmatchedItems; + /** + * True if last call of filterItems() found at least one matching item + */ + bool mHasMatchedItems; +}; + +#endif diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index aa8f2ad04b..e6ecf3c283 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1,3735 +1,3735 @@ -/** - * @file llfloater.cpp - * @brief LLFloater base class - * - * $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$ - */ - -// Floating "windows" within the GL display, like the inventory floater, -// mini-map floater, etc. - -#include "linden_common.h" -#include "llviewereventrecorder.h" -#include "llfloater.h" - -#include "llfocusmgr.h" - -#include "lluictrlfactory.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llcriticaldamp.h" // LLSmoothInterpolation -#include "lldir.h" -#include "lldraghandle.h" -#include "llfloaterreg.h" -#include "llfocusmgr.h" -#include "llresizebar.h" -#include "llresizehandle.h" -#include "llkeyboard.h" -#include "llmenugl.h" // MENU_BAR_HEIGHT -#include "llmodaldialog.h" -#include "lltextbox.h" -#include "llresmgr.h" -#include "llui.h" -#include "llwindow.h" -#include "llstl.h" -#include "llcontrol.h" -#include "lltabcontainer.h" -#include "v2math.h" -#include "lltrans.h" -#include "llhelp.h" -#include "llmultifloater.h" -#include "llsdutil.h" -#include "lluiusage.h" - - -// use this to control "jumping" behavior when Ctrl-Tabbing -const S32 TABBED_FLOATER_OFFSET = 0; - -const F32 LLFloater::CONTEXT_CONE_IN_ALPHA = 0.0f; -const F32 LLFloater::CONTEXT_CONE_OUT_ALPHA = 1.f; -const F32 LLFloater::CONTEXT_CONE_FADE_TIME = 0.08f; - -namespace LLInitParam -{ - void TypeValues::declareValues() - { - declare("relative", LLFloaterEnums::POSITIONING_RELATIVE); - declare("cascading", LLFloaterEnums::POSITIONING_CASCADING); - declare("centered", LLFloaterEnums::POSITIONING_CENTERED); - declare("specified", LLFloaterEnums::POSITIONING_SPECIFIED); - } -} - -std::string LLFloater::sButtonNames[BUTTON_COUNT] = -{ - "llfloater_close_btn", //BUTTON_CLOSE - "llfloater_restore_btn", //BUTTON_RESTORE - "llfloater_minimize_btn", //BUTTON_MINIMIZE - "llfloater_tear_off_btn", //BUTTON_TEAR_OFF - "llfloater_dock_btn", //BUTTON_DOCK - "llfloater_help_btn" //BUTTON_HELP -}; - -std::string LLFloater::sButtonToolTips[BUTTON_COUNT]; - -std::string LLFloater::sButtonToolTipsIndex[BUTTON_COUNT]= -{ -#ifdef LL_DARWIN - "BUTTON_CLOSE_DARWIN", //"Close (Cmd-W)", //BUTTON_CLOSE -#else - "BUTTON_CLOSE_WIN", //"Close (Ctrl-W)", //BUTTON_CLOSE -#endif - "BUTTON_RESTORE", //"Restore", //BUTTON_RESTORE - "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE - "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF - "BUTTON_DOCK", - "BUTTON_HELP" -}; - -LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] = -{ - LLFloater::onClickClose, //BUTTON_CLOSE - LLFloater::onClickMinimize, //BUTTON_RESTORE - LLFloater::onClickMinimize, //BUTTON_MINIMIZE - LLFloater::onClickTearOff, //BUTTON_TEAR_OFF - LLFloater::onClickDock, //BUTTON_DOCK - LLFloater::onClickHelp //BUTTON_HELP -}; - -LLMultiFloater* LLFloater::sHostp = NULL; -bool LLFloater::sQuitting = false; // Flag to prevent storing visibility controls while quitting - -LLFloaterView* gFloaterView = NULL; - -/*==========================================================================*| -// DEV-38598: The fundamental problem with this operation is that it can only -// support a subset of LLSD values. While it's plausible to compare two arrays -// lexicographically, what strict ordering can you impose on maps? -// (LLFloaterTOS's current key is an LLSD map.) - -// Of course something like this is necessary if you want to build a std::set -// or std::map with LLSD keys. Fortunately we're getting by with other -// container types for now. - -//static -bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b) -{ - if (a.type() != b.type()) - { - //LL_ERRS() << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << LL_ENDL; - return false; - } - else if (a.isUndefined()) - return false; - else if (a.isInteger()) - return a.asInteger() < b.asInteger(); - else if (a.isReal()) - return a.asReal() < b.asReal(); - else if (a.isString()) - return a.asString() < b.asString(); - else if (a.isUUID()) - return a.asUUID() < b.asUUID(); - else if (a.isDate()) - return a.asDate() < b.asDate(); - else if (a.isURI()) - return a.asString() < b.asString(); // compare URIs as strings - else if (a.isBoolean()) - return a.asBoolean() < b.asBoolean(); - else - return false; // no valid operation for Binary -} -|*==========================================================================*/ - -bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b) -{ - return llsd_equals(a, b); -} - -//************************************ - -LLFloater::Params::Params() -: title("title"), - short_title("short_title"), - single_instance("single_instance", false), - reuse_instance("reuse_instance", false), - can_resize("can_resize", false), - can_minimize("can_minimize", true), - can_close("can_close", true), - can_drag_on_left("can_drag_on_left", false), - can_tear_off("can_tear_off", true), - save_dock_state("save_dock_state", false), - save_rect("save_rect", false), - save_visibility("save_visibility", false), - can_dock("can_dock", false), - show_title("show_title", true), - auto_close("auto_close", false), - positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE), - header_height("header_height", 0), - legacy_header_height("legacy_header_height", 0), - close_image("close_image"), - restore_image("restore_image"), - minimize_image("minimize_image"), - tear_off_image("tear_off_image"), - dock_image("dock_image"), - help_image("help_image"), - close_pressed_image("close_pressed_image"), - restore_pressed_image("restore_pressed_image"), - minimize_pressed_image("minimize_pressed_image"), - tear_off_pressed_image("tear_off_pressed_image"), - dock_pressed_image("dock_pressed_image"), - help_pressed_image("help_pressed_image"), - open_callback("open_callback"), - close_callback("close_callback"), - follows("follows"), - rel_x("rel_x", 0), - rel_y("rel_y", 0) -{ - changeDefault(visible, false); -} - - -//static -const LLFloater::Params& LLFloater::getDefaultParams() -{ - return LLUICtrlFactory::getDefaultParams(); -} - -//static -void LLFloater::initClass() -{ - // translate tooltips for floater buttons - for (S32 i = 0; i < BUTTON_COUNT; i++) - { - sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] ); - } - - LLControlVariable* ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("ActiveFloaterTransparency").get(); - if (ctrl) - { - ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency)); - updateActiveFloaterTransparency(); - } - - ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("InactiveFloaterTransparency").get(); - if (ctrl) - { - ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency)); - updateInactiveFloaterTransparency(); - } - -} - -// defaults for floater param block pulled from widgets/floater.xml -static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater"); - -LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) -: LLPanel(), // intentionally do not pass params here, see initFromParams - mDragHandle(NULL), - mTitle(p.title), - mShortTitle(p.short_title), - mSingleInstance(p.single_instance), - mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default - mKey(key), - mCanTearOff(p.can_tear_off), - mCanMinimize(p.can_minimize), - mCanClose(p.can_close), - mDragOnLeft(p.can_drag_on_left), - mResizable(p.can_resize), - mAutoClose(p.auto_close), - mPositioning(p.positioning), - mMinWidth(p.min_width), - mMinHeight(p.min_height), - mHeaderHeight(p.header_height), - mLegacyHeaderHeight(p.legacy_header_height), - mDefaultRectForGroup(true), - mMinimized(false), - mForeground(false), - mFirstLook(true), - mButtonScale(1.0f), - mAutoFocus(true), // automatically take focus when opened - mCanDock(false), - mDocked(false), - mTornOff(false), - mHasBeenDraggedWhileMinimized(false), - mPreviousMinimizedBottom(0), - mPreviousMinimizedLeft(0), - mDefaultRelativeX(p.rel_x), - mDefaultRelativeY(p.rel_y), - mMinimizeSignal(NULL) -// mNotificationContext(NULL) -{ - mPosition.setFloater(*this); -// mNotificationContext = new LLFloaterNotificationContext(getHandle()); - - // Clicks stop here. - setMouseOpaque(true); - - // Floaters always draw their background, unlike every other panel. - setBackgroundVisible(true); - - // Floaters start not minimized. When minimized, they save their - // prior rectangle to be used on restore. - mExpandedRect.set(0,0,0,0); - - memset(mButtonsEnabled, 0, BUTTON_COUNT * sizeof(bool)); - memset(mButtons, 0, BUTTON_COUNT * sizeof(LLButton*)); - - addDragHandle(); - addResizeCtrls(); - - initFromParams(p); - - initFloater(p); -} - -// Note: Floaters constructed from XML call init() twice! -void LLFloater::initFloater(const Params& p) -{ - // Close button. - if (mCanClose) - { - mButtonsEnabled[BUTTON_CLOSE] = true; - } - - // Help button: '?' - //SL-14050 Disable all Help question marks - mButtonsEnabled[BUTTON_HELP] = false; - - // Minimize button only for top draggers - if ( !mDragOnLeft && mCanMinimize ) - { - mButtonsEnabled[BUTTON_MINIMIZE] = true; - } - - if(mCanDock) - { - mButtonsEnabled[BUTTON_DOCK] = true; - } - - buildButtons(p); - - // Floaters are created in the invisible state - setVisible(false); - - if (!getParent()) - { - gFloaterView->addChild(this); - } -} - -void LLFloater::addDragHandle() -{ - if (!mDragHandle) - { - if (mDragOnLeft) - { - LLDragHandleLeft::Params p; - p.name("drag"); - p.follows.flags(FOLLOWS_ALL); - p.label(mTitle); - mDragHandle = LLUICtrlFactory::create(p); - } - else // drag on top - { - LLDragHandleTop::Params p; - p.name("Drag Handle"); - p.follows.flags(FOLLOWS_ALL); - p.label(mTitle); - mDragHandle = LLUICtrlFactory::create(p); - } - addChild(mDragHandle); - } - layoutDragHandle(); - applyTitle(); -} - -void LLFloater::layoutDragHandle() -{ - static LLUICachedControl floater_close_box_size ("UIFloaterCloseBoxSize", 0); - S32 close_box_size = mCanClose ? floater_close_box_size : 0; - - LLRect rect; - if (mDragOnLeft) - { - rect.setLeftTopAndSize(0, 0, DRAG_HANDLE_WIDTH, getRect().getHeight() - LLPANEL_BORDER_WIDTH - close_box_size); - } - else // drag on top - { - rect = getLocalRect(); - } - mDragHandle->setShape(rect); - updateTitleButtons(); -} - -// static -void LLFloater::updateActiveFloaterTransparency() -{ - static LLCachedControl active_transparency(*LLUI::getInstance()->mSettingGroups["config"], "ActiveFloaterTransparency", 1.f); - sActiveControlTransparency = active_transparency; -} - -// static -void LLFloater::updateInactiveFloaterTransparency() -{ - static LLCachedControl inactive_transparency(*LLUI::getInstance()->mSettingGroups["config"], "InactiveFloaterTransparency", 0.95f); - sInactiveControlTransparency = inactive_transparency; -} - -void LLFloater::addResizeCtrls() -{ - // Resize bars (sides) - LLResizeBar::Params p; - p.name("resizebar_left"); - p.resizing_view(this); - p.min_size(mMinWidth); - p.side(LLResizeBar::LEFT); - mResizeBar[LLResizeBar::LEFT] = LLUICtrlFactory::create(p); - addChild( mResizeBar[LLResizeBar::LEFT] ); - - p.name("resizebar_top"); - p.min_size(mMinHeight); - p.side(LLResizeBar::TOP); - - mResizeBar[LLResizeBar::TOP] = LLUICtrlFactory::create(p); - addChild( mResizeBar[LLResizeBar::TOP] ); - - p.name("resizebar_right"); - p.min_size(mMinWidth); - p.side(LLResizeBar::RIGHT); - mResizeBar[LLResizeBar::RIGHT] = LLUICtrlFactory::create(p); - addChild( mResizeBar[LLResizeBar::RIGHT] ); - - p.name("resizebar_bottom"); - p.min_size(mMinHeight); - p.side(LLResizeBar::BOTTOM); - mResizeBar[LLResizeBar::BOTTOM] = LLUICtrlFactory::create(p); - addChild( mResizeBar[LLResizeBar::BOTTOM] ); - - // Resize handles (corners) - LLResizeHandle::Params handle_p; - // handles must not be mouse-opaque, otherwise they block hover events - // to other buttons like the close box. JC - handle_p.mouse_opaque(false); - handle_p.min_width(mMinWidth); - handle_p.min_height(mMinHeight); - handle_p.corner(LLResizeHandle::RIGHT_BOTTOM); - mResizeHandle[0] = LLUICtrlFactory::create(handle_p); - addChild(mResizeHandle[0]); - - handle_p.corner(LLResizeHandle::RIGHT_TOP); - mResizeHandle[1] = LLUICtrlFactory::create(handle_p); - addChild(mResizeHandle[1]); - - handle_p.corner(LLResizeHandle::LEFT_BOTTOM); - mResizeHandle[2] = LLUICtrlFactory::create(handle_p); - addChild(mResizeHandle[2]); - - handle_p.corner(LLResizeHandle::LEFT_TOP); - mResizeHandle[3] = LLUICtrlFactory::create(handle_p); - addChild(mResizeHandle[3]); - - layoutResizeCtrls(); -} - -void LLFloater::layoutResizeCtrls() -{ - LLRect rect; - - // Resize bars (sides) - const S32 RESIZE_BAR_THICKNESS = 3; - rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0); - mResizeBar[LLResizeBar::LEFT]->setRect(rect); - - rect = LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS); - mResizeBar[LLResizeBar::TOP]->setRect(rect); - - rect = LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0); - mResizeBar[LLResizeBar::RIGHT]->setRect(rect); - - rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0); - mResizeBar[LLResizeBar::BOTTOM]->setRect(rect); - - // Resize handles (corners) - rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0); - mResizeHandle[0]->setRect(rect); - - rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT); - mResizeHandle[1]->setRect(rect); - - rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ); - mResizeHandle[2]->setRect(rect); - - rect = LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT ); - mResizeHandle[3]->setRect(rect); -} - -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); - } -} - -void LLFloater::destroy() -{ - // LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before - // it was deleted via LLMortician::updateClass(). See EXT-8458. - LLFloaterReg::removeInstance(mInstanceName, mKey); - die(); -} - -// virtual -LLFloater::~LLFloater() -{ - if (!isDead()) - { - // If it's dead, instance is supposed to be already removed, and - // in case of single instance we can remove new one by accident - LLFloaterReg::removeInstance(mInstanceName, mKey); - } - - if( gFocusMgr.childHasKeyboardFocus(this)) - { - // Just in case we might still have focus here, release it. - releaseFocus(); - } - - // This is important so that floaters with persistent rects (i.e., those - // created with rect control rather than an LLRect) are restored in their - // correct, non-minimized positions. - setMinimized( false ); - - delete mDragHandle; - for (S32 i = 0; i < 4; i++) - { - delete mResizeBar[i]; - delete mResizeHandle[i]; - } - - setVisible(false); // We're not visible if we're destroyed - storeVisibilityControl(); - storeDockStateControl(); - delete mMinimizeSignal; -} - -void LLFloater::storeRectControl() -{ - if (!mRectControl.empty()) - { - getControlGroup()->setRect( mRectControl, getRect() ); - } - if (!mPosXControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE) - { - getControlGroup()->setF32( mPosXControl, mPosition.mX ); - } - if (!mPosYControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE) - { - getControlGroup()->setF32( mPosYControl, mPosition.mY ); - } -} - -void LLFloater::storeVisibilityControl() -{ - if( !sQuitting && mVisibilityControl.size() > 1 ) - { - getControlGroup()->setBOOL( mVisibilityControl, getVisible() ); - } -} - -void LLFloater::storeDockStateControl() -{ - if( !sQuitting && mDocStateControl.size() > 1 ) - { - getControlGroup()->setBOOL( mDocStateControl, isDocked() ); - } -} - -// static -std::string LLFloater::getControlName(const std::string& name, const LLSD& key) -{ - std::string ctrl_name = name; - - // Add the key to the control name if appropriate. - if (key.isString() && !key.asString().empty()) - { - ctrl_name += "_" + key.asString(); - } - - return ctrl_name; -} - -// static -LLControlGroup* LLFloater::getControlGroup() -{ - // Floater size, position, visibility, etc are saved in per-account settings. - return LLUI::getInstance()->mSettingGroups["account"]; -} - -void LLFloater::setVisible( bool visible ) -{ - LLPanel::setVisible(visible); // calls onVisibilityChange() - if( visible && mFirstLook ) - { - mFirstLook = false; - } - - if( !visible ) - { - LLUI::getInstance()->removePopup(this); - - if( gFocusMgr.childHasMouseCapture( this ) ) - { - gFocusMgr.setMouseCapture(NULL); - } - } - - for(handle_set_iter_t dependent_it = mDependents.begin(); - dependent_it != mDependents.end(); ) - { - LLFloater* floaterp = dependent_it->get(); - - if (floaterp) - { - floaterp->setVisible(visible); - } - ++dependent_it; - } - - storeVisibilityControl(); -} - - -void LLFloater::setIsSingleInstance(bool is_single_instance) -{ - mSingleInstance = is_single_instance; - if (!mIsReuseInitialized) - { - mReuseInstance = is_single_instance; // reuse single-instance floaters by default - } -} - - -// virtual -void LLFloater::onVisibilityChange ( bool new_visibility ) -{ - if (new_visibility) - { - if (getHost()) - getHost()->setFloaterFlashing(this, false); - } - LLPanel::onVisibilityChange ( new_visibility ); -} - -void LLFloater::openFloater(const LLSD& key) -{ - LL_INFOS() << "Opening floater " << getName() << " full path: " << getPathname() << LL_ENDL; - - LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), true,"floater"); // Last param is event subtype or empty string - - mKey = key; // in case we need to open ourselves again - - if (getSoundFlags() != SILENT - // don't play open sound for hosted (tabbed) windows - && !getHost() - && !getFloaterHost() - && (!getVisible() || isMinimized())) - { - make_ui_sound("UISndWindowOpen"); - } - - //RN: for now, we don't allow rehosting from one multifloater to another - // just need to fix the bugs - if (getFloaterHost() != NULL && getHost() == NULL) - { - // needs a host - // only select tabs if window they are hosted in is visible - getFloaterHost()->addFloater(this, getFloaterHost()->getVisible()); - } - - if (getHost() != NULL) - { - getHost()->setMinimized(false); - getHost()->setVisibleAndFrontmost(mAutoFocus && !getIsChrome()); - getHost()->showFloater(this); - } - else - { - LLFloater* floater_to_stack = LLFloaterReg::getLastFloaterInGroup(mInstanceName); - if (!floater_to_stack) - { - floater_to_stack = LLFloaterReg::getLastFloaterCascading(); - } - applyControlsAndPosition(floater_to_stack); - setMinimized(false); - setVisibleAndFrontmost(mAutoFocus && !getIsChrome()); - } - - mOpenSignal(this, key); - onOpen(key); - - dirtyRect(); -} - -void LLFloater::closeFloater(bool app_quitting) -{ - LL_INFOS() << "Closing floater " << getName() << LL_ENDL; - LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), false,"floater"); // Last param is event subtype or empty string - if (app_quitting) - { - LLFloater::sQuitting = true; - } - - // Always unminimize before trying to close. - // Most of the time the user will never see this state. - setMinimized(false); - - if (canClose()) - { - if (getHost()) - { - ((LLMultiFloater*)getHost())->removeFloater(this); - gFloaterView->addChild(this); - } - - if (getSoundFlags() != SILENT - && getVisible() - && !getHost() - && !app_quitting) - { - make_ui_sound("UISndWindowClose"); - } - - gFocusMgr.clearLastFocusForGroup(this); - - if (hasFocus()) - { - // Do this early, so UI controls will commit before the - // window is taken down. - releaseFocus(); - - // give focus to dependee floater if it exists, and we had focus first - if (isDependent()) - { - LLFloater* dependee = mDependeeHandle.get(); - if (dependee && !dependee->isDead()) - { - dependee->setFocus(true); - } - } - } - - - //If floater is a dependent, remove it from parent (dependee) - LLFloater* dependee = mDependeeHandle.get(); - if (dependee) - { - dependee->removeDependentFloater(this); - } - - // now close dependent floater - while(mDependents.size() > 0) - { - handle_set_iter_t dependent_it = mDependents.begin(); - LLFloater* floaterp = dependent_it->get(); - // normally removeDependentFloater will do this, but in - // case floaterp is somehow invalid or orphaned, erase now - mDependents.erase(dependent_it); - if (floaterp) - { - floaterp->mDependeeHandle = LLHandle(); - floaterp->closeFloater(app_quitting); - } - } - - cleanupHandles(); - - dirtyRect(); - - // Close callbacks - onClose(app_quitting); - mCloseSignal(this, LLSD(app_quitting)); - - // Hide or Destroy - if (mSingleInstance) - { - // Hide the instance - if (getHost()) - { - getHost()->setVisible(false); - } - else - { - setVisible(false); - if (!mReuseInstance) - { - destroy(); - } - } - } - else - { - setVisible(false); // hide before destroying (so onVisibilityChange() gets called) - if (!mReuseInstance) - { - destroy(); - } - } - } -} - -/*virtual*/ -void LLFloater::closeHostedFloater() -{ - // When toggling *visibility*, close the host instead of the floater when hosted - if (getHost()) - { - getHost()->closeFloater(); - } - else - { - closeFloater(); - } -} - -/*virtual*/ -void LLFloater::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLPanel::reshape(width, height, called_from_parent); -} - -// virtual -void LLFloater::translate(S32 x, S32 y) -{ - LLView::translate(x, y); - - if (!mTranslateWithDependents || mDependents.empty()) - return; - - for (const LLHandle& handle : mDependents) - { - LLFloater* floater = handle.get(); - if (floater && floater->getSnapTarget() == getHandle()) - { - floater->LLView::translate(x, y); - } - } -} - -void LLFloater::releaseFocus() -{ - LLUI::getInstance()->removePopup(this); - - setFocus(false); - - if( gFocusMgr.childHasMouseCapture( this ) ) - { - gFocusMgr.setMouseCapture(NULL); - } -} - - -void LLFloater::setResizeLimits( S32 min_width, S32 min_height ) -{ - mMinWidth = min_width; - mMinHeight = min_height; - - for( S32 i = 0; i < 4; i++ ) - { - if( mResizeBar[i] ) - { - if (i == LLResizeBar::LEFT || i == LLResizeBar::RIGHT) - { - mResizeBar[i]->setResizeLimits( min_width, S32_MAX ); - } - else - { - mResizeBar[i]->setResizeLimits( min_height, S32_MAX ); - } - } - if( mResizeHandle[i] ) - { - mResizeHandle[i]->setResizeLimits( min_width, min_height ); - } - } -} - - -void LLFloater::center() -{ - if(getHost()) - { - // hosted floaters can't move - return; - } - centerWithin(gFloaterView->getRect()); -} - -LLMultiFloater* LLFloater::getHost() -{ - return (LLMultiFloater*)mHostHandle.get(); -} - -void LLFloater::applyControlsAndPosition(LLFloater* other) -{ - if (!applyDockState()) - { - if (!applyRectControl()) - { - applyPositioning(other, true); - } - } -} - -bool LLFloater::applyRectControl() -{ - bool saved_rect = false; - - LLRect screen_rect = calcScreenRect(); - mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); - - LLFloater* last_in_group = LLFloaterReg::getLastFloaterInGroup(mInstanceName); - if (last_in_group && last_in_group != this) - { - // other floaters in our group, position ourselves relative to them and don't save the rect - if (mDefaultRectForGroup) - { - mRectControl.clear(); - } - mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP; - } - else - { - bool rect_specified = false; - if (!mRectControl.empty()) - { - // If we have a saved rect, use it - const LLRect& rect = getControlGroup()->getRect(mRectControl); - if (rect.notEmpty()) saved_rect = true; - if (saved_rect) - { - setOrigin(rect.mLeft, rect.mBottom); - - if (mResizable) - { - reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight())); - } - mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; - LLRect screen_rect = calcScreenRect(); - mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); - rect_specified = true; - } - } - - LLControlVariablePtr x_control = getControlGroup()->getControl(mPosXControl); - LLControlVariablePtr y_control = getControlGroup()->getControl(mPosYControl); - if (x_control.notNull() - && y_control.notNull() - && !x_control->isDefault() - && !y_control->isDefault()) - { - mPosition.mX = x_control->getValue().asReal(); - mPosition.mY = y_control->getValue().asReal(); - mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; - applyRelativePosition(); - - saved_rect = true; - } - else if ((mDefaultRelativeX != 0) && (mDefaultRelativeY != 0)) - { - mPosition.mX = mDefaultRelativeX; - mPosition.mY = mDefaultRelativeY; - mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; - applyRelativePosition(); - - saved_rect = true; - } - - // remember updated position - if (rect_specified) - { - storeRectControl(); - } - } - - if (saved_rect) - { - // propagate any derived positioning data back to settings file - storeRectControl(); - } - - - return saved_rect; -} - -bool LLFloater::applyDockState() -{ - bool docked = false; - - if (mDocStateControl.size() > 1) - { - docked = getControlGroup()->getBOOL(mDocStateControl); - setDocked(docked); - } - - return docked; -} - -void LLFloater::applyPositioning(LLFloater* other, bool on_open) -{ - // Otherwise position according to the positioning code - switch (mPositioning) - { - case LLFloaterEnums::POSITIONING_CENTERED: - center(); - break; - - case LLFloaterEnums::POSITIONING_SPECIFIED: - break; - - case LLFloaterEnums::POSITIONING_CASCADING: - if (!on_open) - { - applyRelativePosition(); - } - // fall through - case LLFloaterEnums::POSITIONING_CASCADE_GROUP: - if (on_open) - { - if (other != NULL && other != this) - { - stackWith(*other); - } - else - { - static const U32 CASCADING_FLOATER_HOFFSET = 0; - static const U32 CASCADING_FLOATER_VOFFSET = 0; - - const LLRect& snap_rect = gFloaterView->getSnapRect(); - - const S32 horizontal_offset = CASCADING_FLOATER_HOFFSET; - const S32 vertical_offset = snap_rect.getHeight() - CASCADING_FLOATER_VOFFSET; - - S32 rect_height = getRect().getHeight(); - setOrigin(horizontal_offset, vertical_offset - rect_height); - - translate(snap_rect.mLeft, snap_rect.mBottom); - } - setFollows(FOLLOWS_TOP | FOLLOWS_LEFT); - } - break; - - case LLFloaterEnums::POSITIONING_RELATIVE: - { - applyRelativePosition(); - - break; - } - default: - // Do nothing - break; - } -} - -void LLFloater::applyTitle() -{ - if (!mDragHandle) - { - return; - } - - if (isMinimized() && !mShortTitle.empty()) - { - mDragHandle->setTitle( mShortTitle ); - } - else - { - mDragHandle->setTitle ( mTitle ); - } - - if (getHost()) - { - getHost()->updateFloaterTitle(this); - } -} - -std::string LLFloater::getCurrentTitle() const -{ - return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null; -} - -void LLFloater::setTitle( const std::string& title ) -{ - mTitle = title; - applyTitle(); -} - -std::string LLFloater::getTitle() const -{ - if (mTitle.empty()) - { - return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null; - } - else - { - return mTitle; - } -} - -void LLFloater::setShortTitle( const std::string& short_title ) -{ - mShortTitle = short_title; - applyTitle(); -} - -std::string LLFloater::getShortTitle() const -{ - if (mShortTitle.empty()) - { - return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null; - } - else - { - return mShortTitle; - } -} - -bool LLFloater::canSnapTo(const LLView* other_view) -{ - if (NULL == other_view) - { - LL_WARNS() << "other_view is NULL" << LL_ENDL; - return false; - } - - if (other_view != getParent()) - { - const LLFloater* other_floaterp = dynamic_cast(other_view); - if (other_floaterp - && other_floaterp->getSnapTarget() == getHandle() - && mDependents.find(other_floaterp->getHandle()) != mDependents.end()) - { - // this is a dependent that is already snapped to us, so don't snap back to it - return false; - } - } - - return LLPanel::canSnapTo(other_view); -} - -void LLFloater::setSnappedTo(const LLView* snap_view) -{ - if (!snap_view || snap_view == getParent()) - { - clearSnapTarget(); - } - else - { - //RN: assume it's a floater as it must be a sibling to our parent floater - const LLFloater* floaterp = dynamic_cast(snap_view); - if (floaterp) - { - setSnapTarget(floaterp->getHandle()); - } - } -} - -void LLFloater::handleReshape(const LLRect& new_rect, bool by_user) -{ - const LLRect old_rect = getRect(); - LLView::handleReshape(new_rect, by_user); - - if (by_user && !getHost()) - { - LLFloaterView * floaterVp = dynamic_cast(getParent()); - if (floaterVp) - { - floaterVp->adjustToFitScreen(this, !isMinimized()); - } - } - - // if not minimized, adjust all snapped dependents to new shape - if (!isMinimized()) - { - if (by_user) - { - if (isDocked()) - { - setDocked( false, false); - } - mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; - LLRect screen_rect = calcScreenRect(); - mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); - } - storeRectControl(); - - // gather all snapped dependents - for(handle_set_iter_t dependent_it = mDependents.begin(); - dependent_it != mDependents.end(); ++dependent_it) - { - LLFloater* floaterp = dependent_it->get(); - // is a dependent snapped to us? - if (floaterp && floaterp->getSnapTarget() == getHandle()) - { - S32 delta_x = 0; - S32 delta_y = 0; - // check to see if it snapped to right or top, and move if dependee floater is resizing - LLRect dependent_rect = floaterp->getRect(); - if (dependent_rect.mLeft - getRect().mLeft >= old_rect.getWidth() || // dependent on my right? - dependent_rect.mRight == getRect().mLeft + old_rect.getWidth()) // dependent aligned with my right - { - // was snapped directly onto right side or aligned with it - delta_x += new_rect.getWidth() - old_rect.getWidth(); - } - if (dependent_rect.mBottom - getRect().mBottom >= old_rect.getHeight() || - dependent_rect.mTop == getRect().mBottom + old_rect.getHeight()) - { - // was snapped directly onto top side or aligned with it - delta_y += new_rect.getHeight() - old_rect.getHeight(); - } - - // take translation of dependee floater into account as well - delta_x += new_rect.mLeft - old_rect.mLeft; - delta_y += new_rect.mBottom - old_rect.mBottom; - - dependent_rect.translate(delta_x, delta_y); - floaterp->setShape(dependent_rect, by_user); - } - } - } - else - { - // If minimized, and origin has changed, set - // mHasBeenDraggedWhileMinimized to true - if ((new_rect.mLeft != old_rect.mLeft) || - (new_rect.mBottom != old_rect.mBottom)) - { - mHasBeenDraggedWhileMinimized = true; - } - } -} - -void LLFloater::setMinimized(bool minimize) -{ - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - static LLUICachedControl minimized_width ("UIMinimizedWidth", 0); - - if (minimize == mMinimized) return; - - if (mMinimizeSignal) - { - (*mMinimizeSignal)(this, LLSD(minimize)); - } - - if (minimize) - { - // minimized flag should be turned on before release focus - mMinimized = true; - mExpandedRect = getRect(); - - // If the floater has been dragged while minimized in the - // past, then locate it at its previous minimized location. - // Otherwise, ask the view for a minimize position. - if (mHasBeenDraggedWhileMinimized) - { - setOrigin(mPreviousMinimizedLeft, mPreviousMinimizedBottom); - } - else - { - S32 left, bottom; - gFloaterView->getMinimizePosition(&left, &bottom); - setOrigin( left, bottom ); - } - - if (mButtonsEnabled[BUTTON_MINIMIZE]) - { - mButtonsEnabled[BUTTON_MINIMIZE] = false; - mButtonsEnabled[BUTTON_RESTORE] = true; - } - - setBorderVisible(true); - - for(handle_set_iter_t dependent_it = mDependents.begin(); - dependent_it != mDependents.end(); - ++dependent_it) - { - LLFloater* floaterp = dependent_it->get(); - if (floaterp) - { - if (floaterp->isMinimizeable()) - { - floaterp->setMinimized(true); - } - else if (!floaterp->isMinimized()) - { - floaterp->setVisible(false); - } - } - } - - // Lose keyboard focus when minimized - releaseFocus(); - - for (S32 i = 0; i < 4; i++) - { - if (mResizeBar[i] != NULL) - { - mResizeBar[i]->setEnabled(false); - } - if (mResizeHandle[i] != NULL) - { - mResizeHandle[i]->setEnabled(false); - } - } - - // Reshape *after* setting mMinimized - reshape( minimized_width, floater_header_size, true); - } - else - { - // If this window has been dragged while minimized (at any time), - // remember its position for the next time it's minimized. - if (mHasBeenDraggedWhileMinimized) - { - const LLRect& currentRect = getRect(); - mPreviousMinimizedLeft = currentRect.mLeft; - mPreviousMinimizedBottom = currentRect.mBottom; - } - - setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom ); - if (mButtonsEnabled[BUTTON_RESTORE]) - { - mButtonsEnabled[BUTTON_MINIMIZE] = true; - mButtonsEnabled[BUTTON_RESTORE] = false; - } - - // show dependent floater - for(handle_set_iter_t dependent_it = mDependents.begin(); - dependent_it != mDependents.end(); - ++dependent_it) - { - LLFloater* floaterp = dependent_it->get(); - if (floaterp) - { - floaterp->setMinimized(false); - floaterp->setVisible(true); - } - } - - for (S32 i = 0; i < 4; i++) - { - if (mResizeBar[i] != NULL) - { - mResizeBar[i]->setEnabled(isResizable()); - } - if (mResizeHandle[i] != NULL) - { - mResizeHandle[i]->setEnabled(isResizable()); - } - } - - mMinimized = false; - setFrontmost(); - // Reshape *after* setting mMinimized - reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), true ); - } - - make_ui_sound("UISndWindowClose"); - updateTitleButtons(); - applyTitle (); -} - -void LLFloater::setFocus( bool b ) -{ - if (b && getIsChrome()) - { - return; - } - LLView* last_focus = gFocusMgr.getLastFocusForGroup(this); - // a descendent already has focus - bool child_had_focus = hasFocus(); - - // give focus to first valid descendent - LLPanel::setFocus(b); - - if (b) - { - // only push focused floaters to front of stack if not in midst of ctrl-tab cycle - LLFloaterView * parent = dynamic_cast(getParent()); - if (!getHost() && parent && !parent->getCycleMode()) - { - if (!isFrontmost()) - { - setFrontmost(); - } - } - - // when getting focus, delegate to last descendent which had focus - if (last_focus && !child_had_focus && - last_focus->isInEnabledChain() && - last_focus->isInVisibleChain()) - { - // *FIX: should handle case where focus doesn't stick - last_focus->setFocus(true); - } - } - updateTransparency(b ? TT_ACTIVE : TT_INACTIVE); -} - -// virtual -void LLFloater::setRect(const LLRect &rect) -{ - LLPanel::setRect(rect); - layoutDragHandle(); - layoutResizeCtrls(); -} - -// virtual -void LLFloater::setIsChrome(bool is_chrome) -{ - // chrome floaters don't take focus at all - if (is_chrome) - { - // remove focus if we're changing to chrome - setFocus(false); - // can't Ctrl-Tab to "chrome" floaters - setFocusRoot(false); - mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome))); - } - - LLPanel::setIsChrome(is_chrome); -} - -// Change the draw style to account for the foreground state. -void LLFloater::setForeground(bool front) -{ - if (front != mForeground) - { - mForeground = front; - if (mDragHandle) - mDragHandle->setForeground( front ); - - if (!front) - { - releaseFocus(); - } - - setBackgroundOpaque( front ); - } -} - -void LLFloater::cleanupHandles() -{ - // remove handles to non-existent dependents - for(handle_set_iter_t dependent_it = mDependents.begin(); - dependent_it != mDependents.end(); ) - { - LLFloater* floaterp = dependent_it->get(); - if (!floaterp) - { - dependent_it = mDependents.erase(dependent_it); - } - else - { - ++dependent_it; - } - } -} - -void LLFloater::setHost(LLMultiFloater* host) -{ - if (mHostHandle.isDead() && host) - { - // make buttons smaller for hosted windows to differentiate from parent - mButtonScale = 0.9f; - - // add tear off button - if (mCanTearOff) - { - mButtonsEnabled[BUTTON_TEAR_OFF] = true; - } - } - else if (!mHostHandle.isDead() && !host) - { - mButtonScale = 1.f; - //mButtonsEnabled[BUTTON_TEAR_OFF] = false; - } - if (host) - { - mHostHandle = host->getHandle(); - mLastHostHandle = host->getHandle(); - } - else - { - mHostHandle.markDead(); - } - - updateTitleButtons(); -} - -void LLFloater::moveResizeHandlesToFront() -{ - for( S32 i = 0; i < 4; i++ ) - { - if( mResizeBar[i] ) - { - sendChildToFront(mResizeBar[i]); - } - } - - for( S32 i = 0; i < 4; i++ ) - { - if( mResizeHandle[i] ) - { - sendChildToFront(mResizeHandle[i]); - } - } -} - -/*virtual*/ -bool LLFloater::isFrontmost() -{ - LLFloaterView* floater_view = getParentByType(); - return getVisible() - && (floater_view - && floater_view->getFrontmost() == this); -} - -void LLFloater::addDependentFloater(LLFloater* floaterp, bool reposition, bool resize) -{ - mDependents.insert(floaterp->getHandle()); - floaterp->mDependeeHandle = getHandle(); - - if (reposition) - { - LLRect rect = gFloaterView->findNeighboringPosition(this, floaterp); - if (resize) - { - const LLRect& base = getRect(); - if (rect.mTop == base.mTop) - rect.mBottom = base.mBottom; - else if (rect.mLeft == base.mLeft) - rect.mRight = base.mRight; - floaterp->reshape(rect.getWidth(), rect.getHeight(), false); - } - floaterp->setRect(rect); - floaterp->setSnapTarget(getHandle()); - } - gFloaterView->adjustToFitScreen(floaterp, false, true); - if (floaterp->isFrontmost()) - { - // make sure to bring self and sibling floaters to front - gFloaterView->bringToFront(floaterp, floaterp->getAutoFocus() && !getIsChrome()); - } -} - -void LLFloater::addDependentFloater(LLHandle dependent, bool reposition, bool resize) -{ - LLFloater* dependent_floaterp = dependent.get(); - if(dependent_floaterp) - { - addDependentFloater(dependent_floaterp, reposition, resize); - } -} - -void LLFloater::removeDependentFloater(LLFloater* floaterp) -{ - mDependents.erase(floaterp->getHandle()); - floaterp->mDependeeHandle = LLHandle(); -} - -void LLFloater::fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels) -{ - LLRect total_rect = getRect(); - - for (const LLHandle& handle : mDependents) - { - LLFloater* floater = handle.get(); - if (floater && floater->getSnapTarget() == getHandle()) - { - total_rect.unionWith(floater->getRect()); - } - } - - S32 delta_left = left.notEmpty() ? left.mRight - total_rect.mRight : 0; - S32 delta_bottom = bottom.notEmpty() ? bottom.mTop - total_rect.mTop : 0; - S32 delta_right = right.notEmpty() ? right.mLeft - total_rect.mLeft : 0; - - // move floater with dependings fully onscreen - mTranslateWithDependents = true; - if (translateRectIntoRect(total_rect, constraint, min_overlap_pixels)) - { - clearSnapTarget(); - } - else if (delta_left > 0 && total_rect.mTop < left.mTop && total_rect.mBottom > left.mBottom) - { - translate(delta_left, 0); - } - else if (delta_bottom > 0 && total_rect.mLeft > bottom.mLeft && total_rect.mRight < bottom.mRight) - { - translate(0, delta_bottom); - } - else if (delta_right < 0 && total_rect.mTop < right.mTop && total_rect.mBottom > right.mBottom) - { - translate(delta_right, 0); - } - mTranslateWithDependents = false; -} - -bool LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index) -{ - if( mButtonsEnabled[index] ) - { - LLButton* my_butt = mButtons[index]; - S32 local_x = x - my_butt->getRect().mLeft; - S32 local_y = y - my_butt->getRect().mBottom; - - if ( - my_butt->pointInView(local_x, local_y) && - my_butt->handleMouseDown(local_x, local_y, mask)) - { - // the button handled it - return true; - } - } - return false; -} - -bool LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - LLPanel::handleScrollWheel(x,y,clicks); - return true;//always -} - -// virtual -bool LLFloater::handleMouseUp(S32 x, S32 y, MASK mask) -{ - LL_DEBUGS() << "LLFloater::handleMouseUp calling LLPanel (really LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; - bool handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView - if (handled) { - LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); - } - return handled; -} - -// virtual -bool LLFloater::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if( mMinimized ) - { - // Offer the click to titlebar buttons. - // Note: this block and the offerClickToButton helper method can be removed - // because the parent container will handle it for us but we'll keep it here - // for safety until after reworking the panel code to manage hidden children. - if(offerClickToButton(x, y, mask, BUTTON_CLOSE)) return true; - if(offerClickToButton(x, y, mask, BUTTON_RESTORE)) return true; - if(offerClickToButton(x, y, mask, BUTTON_TEAR_OFF)) return true; - if(offerClickToButton(x, y, mask, BUTTON_DOCK)) return true; - - setFrontmost(true, false); - // Otherwise pass to drag handle for movement - return mDragHandle->handleMouseDown(x, y, mask); - } - else - { - bringToFront( x, y ); - bool handled = LLPanel::handleMouseDown( x, y, mask ); - if (handled) { - LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); - } - return handled; - } -} - -// virtual -bool LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - bool was_minimized = mMinimized; - bringToFront( x, y ); - return was_minimized || LLPanel::handleRightMouseDown( x, y, mask ); -} - -bool LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - bringToFront( x, y ); - return LLPanel::handleMiddleMouseDown( x, y, mask ); -} - - -// virtual -bool LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - bool was_minimized = mMinimized; - setMinimized(false); - return was_minimized || LLPanel::handleDoubleClick(x, y, mask); -} - -// virtual -void LLFloater::bringToFront( S32 x, S32 y ) -{ - if (getVisible() && pointInView(x, y)) - { - LLMultiFloater* hostp = getHost(); - if (hostp) - { - hostp->showFloater(this); - } - else - { - LLFloaterView* parent = dynamic_cast( getParent() ); - if (parent) - { - parent->bringToFront(this, !getIsChrome()); - } - } - } -} - -// virtual -void LLFloater::goneFromFront() -{ - if (mAutoClose) - { - closeFloater(); - } -} - -// virtual -void LLFloater::setVisibleAndFrontmost(bool take_focus,const LLSD& key) -{ - LLUIUsage::instance().logFloater(getInstanceName()); - LLMultiFloater* hostp = getHost(); - if (hostp) - { - hostp->setVisible(true); - hostp->setFrontmost(take_focus); - } - else - { - setVisible(true); - setFrontmost(take_focus); - } -} - -void LLFloater::setFrontmost(bool take_focus, bool restore) -{ - LLMultiFloater* hostp = getHost(); - if (hostp) - { - // this will bring the host floater to the front and select - // the appropriate panel - hostp->showFloater(this); - } - else - { - // there are more than one floater view - // so we need to query our parent directly - LLFloaterView * parent = dynamic_cast( getParent() ); - if (parent) - { - parent->bringToFront(this, take_focus, restore); - } - - // Make sure to set the appropriate transparency type (STORM-732). - updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); - } -} - -void LLFloater::setCanDock(bool b) -{ - if(b != mCanDock) - { - mCanDock = b; - if(mCanDock) - { - mButtonsEnabled[BUTTON_DOCK] = !mDocked; - } - else - { - mButtonsEnabled[BUTTON_DOCK] = false; - } - } - updateTitleButtons(); -} - -void LLFloater::setDocked(bool docked, bool pop_on_undock) -{ - if(docked != mDocked && mCanDock) - { - mDocked = docked; - mButtonsEnabled[BUTTON_DOCK] = !mDocked; - - if (mDocked) - { - setMinimized(false); - mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; - } - - updateTitleButtons(); - - storeDockStateControl(); - } - -} - -// static -void LLFloater::onClickMinimize(LLFloater* self) -{ - if (!self) - return; - self->setMinimized( !self->isMinimized() ); -} - -void LLFloater::onClickTearOff(LLFloater* self) -{ - if (!self) - return; - S32 floater_header_size = self->mHeaderHeight; - LLMultiFloater* host_floater = self->getHost(); - if (host_floater) //Tear off - { - LLRect new_rect; - host_floater->removeFloater(self); - // reparent to floater view - gFloaterView->addChild(self); - - self->openFloater(self->getKey()); - if (self->mSaveRect && !self->mRectControl.empty()) - { - self->applyRectControl(); - } - else - { // only force position for floaters that don't have that data saved - new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight()); - self->setRect(new_rect); - } - gFloaterView->adjustToFitScreen(self, false); - // give focus to new window to keep continuity for the user - self->setFocus(true); - self->setTornOff(true); - } - else //Attach to parent. - { - LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get(); - if (new_host) - { - if (self->mSaveRect) - { - LLRect screen_rect = self->calcScreenRect(); - self->mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); - self->storeRectControl(); - } - self->setMinimized(false); // to reenable minimize button if it was minimized - new_host->showFloater(self); - // make sure host is visible - new_host->openFloater(new_host->getKey()); - } - self->setTornOff(false); - } - self->updateTitleButtons(); - self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); -} - -// static -void LLFloater::onClickDock(LLFloater* self) -{ - if(self && self->mCanDock) - { - self->setDocked(!self->mDocked, true); - } -} - -// static -void LLFloater::onClickHelp( LLFloater* self ) -{ - if (self && LLUI::getInstance()->mHelpImpl) - { - // find the current help context for this floater - std::string help_topic; - if (self->findHelpTopic(help_topic)) - { - LLUI::getInstance()->mHelpImpl->showTopic(help_topic); - } - } -} - -void LLFloater::initRectControl() -{ - // save_rect and save_visibility only apply to registered floaters - if (mSaveRect) - { - std::string ctrl_name = getControlName(mInstanceName, mKey); - mRectControl = LLFloaterReg::declareRectControl(ctrl_name); - mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name); - mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name); - } -} - -// static -void LLFloater::closeFrontmostFloater() -{ - LLFloater* floater_to_close = gFloaterView->getFrontmostClosableFloater(); - if(floater_to_close) - { - floater_to_close->closeFloater(); - } - - // if nothing took focus after closing focused floater - // give it to next floater (to allow closing multiple windows via keyboard in rapid succession) - if (gFocusMgr.getKeyboardFocus() == NULL) - { - // HACK: use gFloaterView directly in case we are using Ctrl-W to close snapshot window - // which sits in gSnapshotFloaterView, and needs to pass focus on to normal floater view - gFloaterView->focusFrontFloater(); - } -} - - -// static -void LLFloater::onClickClose( LLFloater* self ) -{ - if (!self) - return; - self->onClickCloseBtn(); -} - -void LLFloater::onClickCloseBtn(bool app_quitting) -{ - closeFloater(false); -} - - -// virtual -void LLFloater::draw() -{ - const F32 alpha = getCurrentTransparency(); - - // draw background - if( isBackgroundVisible() ) - { - drawShadow(this); - - S32 left = LLPANEL_BORDER_WIDTH; - S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH; - S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH; - S32 bottom = LLPANEL_BORDER_WIDTH; - - LLUIImage* image = NULL; - LLColor4 color; - LLColor4 overlay_color; - if (isBackgroundOpaque()) - { - // NOTE: image may not be set - image = getBackgroundImage(); - color = getBackgroundColor(); - overlay_color = getBackgroundImageOverlay(); - } - else - { - image = getTransparentImage(); - color = getTransparentColor(); - overlay_color = getTransparentImageOverlay(); - } - - if (image) - { - // We're using images for this floater's backgrounds - image->draw(getLocalRect(), overlay_color % alpha); - } - else - { - // We're not using images, use old-school flat colors - gl_rect_2d( left, top, right, bottom, color % alpha ); - - // draw highlight on title bar to indicate focus. RDW - if(hasFocus() - && !getIsChrome() - && !getCurrentTitle().empty()) - { - static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor"); - - const LLFontGL* font = LLFontGL::getFontSansSerif(); - LLRect r = getRect(); - gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - font->getLineHeight() - 1, - titlebar_focus_color % alpha, 0, true); - } - } - } - - LLPanel::updateDefaultBtn(); - - if( getDefaultButton() ) - { - if (hasFocus() && getDefaultButton()->getEnabled()) - { - LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); - // is this button a direct descendent and not a nested widget (e.g. checkbox)? - bool focus_is_child_button = dynamic_cast(focus_ctrl) != NULL && dynamic_cast(focus_ctrl)->getParent() == this; - // only enable default button when current focus is not a button - getDefaultButton()->setBorderEnabled(!focus_is_child_button); - } - else - { - getDefaultButton()->setBorderEnabled(false); - } - } - if (isMinimized()) - { - for (S32 i = 0; i < BUTTON_COUNT; i++) - { - drawChild(mButtons[i]); - } - drawChild(mDragHandle, 0, 0, true); - } - else - { - // don't call LLPanel::draw() since we've implemented custom background rendering - LLView::draw(); - } - - // update tearoff button for torn off floaters - // when last host goes away - if (mCanTearOff && !getHost()) - { - LLFloater* old_host = mLastHostHandle.get(); - if (!old_host) - { - setCanTearOff(false); - } - } -} - -void LLFloater::drawShadow(LLPanel* panel) -{ - S32 left = LLPANEL_BORDER_WIDTH; - S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH; - S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH; - S32 bottom = LLPANEL_BORDER_WIDTH; - - static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow"); - LLColor4 shadow_color = shadow_color_cached; - F32 shadow_offset = (F32)DROP_SHADOW_FLOATER; - - if (!panel->isBackgroundOpaque()) - { - shadow_offset *= 0.2f; - shadow_color.mV[VALPHA] *= 0.5f; - } - gl_drop_shadow(left, top, right, bottom, - shadow_color % getCurrentTransparency(), - ll_round(shadow_offset)); -} - -void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type) -{ - if (!view) return; - child_list_t children = *view->getChildList(); - child_list_t::iterator it = children.begin(); - - LLUICtrl* ctrl = dynamic_cast(view); - if (ctrl) - { - ctrl->setTransparencyType(transparency_type); - } - - for(; it != children.end(); ++it) - { - updateTransparency(*it, transparency_type); - } -} - -void LLFloater::updateTransparency(ETypeTransparency transparency_type) -{ - updateTransparency(this, transparency_type); -} - -void LLFloater::setCanMinimize(bool can_minimize) -{ - // if removing minimize/restore button programmatically, - // go ahead and unminimize floater - mCanMinimize = can_minimize; - if (!can_minimize) - { - setMinimized(false); - } - - mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized(); - mButtonsEnabled[BUTTON_RESTORE] = can_minimize && isMinimized(); - - updateTitleButtons(); -} - -void LLFloater::setCanClose(bool can_close) -{ - mCanClose = can_close; - mButtonsEnabled[BUTTON_CLOSE] = can_close; - - updateTitleButtons(); -} - -void LLFloater::setCanTearOff(bool can_tear_off) -{ - mCanTearOff = can_tear_off; - mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead(); - - updateTitleButtons(); -} - - -void LLFloater::setCanResize(bool can_resize) -{ - mResizable = can_resize; - enableResizeCtrls(can_resize); -} - -void LLFloater::setCanDrag(bool can_drag) -{ - // if we delete drag handle, we no longer have access to the floater's title - // so just enable/disable it - if (!can_drag && mDragHandle->getEnabled()) - { - mDragHandle->setEnabled(false); - } - else if (can_drag && !mDragHandle->getEnabled()) - { - mDragHandle->setEnabled(true); - } -} - -bool LLFloater::getCanDrag() -{ - return mDragHandle->getEnabled(); -} - - -void LLFloater::updateTitleButtons() -{ - static LLUICachedControl floater_close_box_size ("UIFloaterCloseBoxSize", 0); - static LLUICachedControl close_box_from_top ("UICloseBoxFromTop", 0); - LLRect buttons_rect; - S32 button_count = 0; - for (S32 i = 0; i < BUTTON_COUNT; i++) - { - if (!mButtons[i]) - { - continue; - } - - bool enabled = mButtonsEnabled[i]; - if (i == BUTTON_HELP) - { - // don't show the help button if the floater is minimized - // or if it is a docked tear-off floater - if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff)) - { - enabled = false; - } - } - if (i == BUTTON_CLOSE && mButtonScale != 1.f) - { - //*HACK: always render close button for hosted floaters so - //that users don't accidentally hit the button when - //closing multiple windows in the chatterbox - enabled = true; - } - - mButtons[i]->setEnabled(enabled); - - if (enabled) - { - button_count++; - - LLRect btn_rect; - if (mDragOnLeft) - { - btn_rect.setLeftTopAndSize( - LLPANEL_BORDER_WIDTH, - getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * button_count, - ll_round((F32)floater_close_box_size * mButtonScale), - ll_round((F32)floater_close_box_size * mButtonScale)); - } - else - { - btn_rect.setLeftTopAndSize( - getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * button_count, - getRect().getHeight() - close_box_from_top, - ll_round((F32)floater_close_box_size * mButtonScale), - ll_round((F32)floater_close_box_size * mButtonScale)); - } - - // first time here, init 'buttons_rect' - if(1 == button_count) - { - buttons_rect = btn_rect; - } - else - { - // if mDragOnLeft=true then buttons are on top-left side vertically aligned - // title is not displayed in this case, calculating 'buttons_rect' for future use - mDragOnLeft ? buttons_rect.mBottom -= btn_rect.mBottom : - buttons_rect.mLeft = btn_rect.mLeft; - } - mButtons[i]->setRect(btn_rect); - mButtons[i]->setVisible(true); - // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater - mButtons[i]->setTabStop(i == BUTTON_RESTORE); - } - else - { - mButtons[i]->setVisible(false); - } - } - if (mDragHandle) - { - localRectToOtherView(buttons_rect, &buttons_rect, mDragHandle); - mDragHandle->setButtonsRect(buttons_rect); - } -} - -void LLFloater::drawConeToOwner(F32 &context_cone_opacity, - F32 max_cone_opacity, - LLView *owner_view, - F32 fade_time, - F32 contex_cone_in_alpha, - F32 contex_cone_out_alpha) -{ - if (owner_view - && owner_view->isInVisibleChain() - && hasFocus() - && context_cone_opacity > 0.001f - && gFocusMgr.childHasKeyboardFocus(this)) - { - // draw cone of context pointing back to owner (e.x. texture swatch) - LLRect owner_rect; - owner_view->localRectToOtherView(owner_view->getLocalRect(), &owner_rect, this); - LLRect local_rect = getLocalRect(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLEnable(GL_CULL_FACE); - gGL.begin(LLRender::QUADS); - { - gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); - gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop); - gGL.vertex2i(owner_rect.mRight, owner_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); - gGL.vertex2i(local_rect.mRight, local_rect.mTop); - gGL.vertex2i(local_rect.mLeft, local_rect.mTop); - - gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); - gGL.vertex2i(local_rect.mLeft, local_rect.mTop); - gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); - gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); - gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom); - gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop); - - gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); - gGL.vertex2i(local_rect.mRight, local_rect.mBottom); - gGL.vertex2i(local_rect.mRight, local_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); - gGL.vertex2i(owner_rect.mRight, owner_rect.mTop); - gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom); - - - gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); - gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); - gGL.vertex2i(local_rect.mRight, local_rect.mBottom); - gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); - gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom); - gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom); - } - gGL.end(); - } - - if (gFocusMgr.childHasMouseCapture(getDragHandle())) - { - context_cone_opacity = lerp(context_cone_opacity, max_cone_opacity, LLSmoothInterpolation::getInterpolant(fade_time)); - } - else - { - context_cone_opacity = lerp(context_cone_opacity, 0.f, LLSmoothInterpolation::getInterpolant(fade_time)); - } -} - -void LLFloater::buildButtons(const Params& floater_params) -{ - static LLUICachedControl floater_close_box_size ("UIFloaterCloseBoxSize", 0); - static LLUICachedControl close_box_from_top ("UICloseBoxFromTop", 0); - for (S32 i = 0; i < BUTTON_COUNT; i++) - { - if (mButtons[i]) - { - removeChild(mButtons[i]); - delete mButtons[i]; - mButtons[i] = NULL; - } - - LLRect btn_rect; - if (mDragOnLeft) - { - btn_rect.setLeftTopAndSize( - LLPANEL_BORDER_WIDTH, - getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * (i + 1), - ll_round(floater_close_box_size * mButtonScale), - ll_round(floater_close_box_size * mButtonScale)); - } - else - { - btn_rect.setLeftTopAndSize( - getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * (i + 1), - getRect().getHeight() - close_box_from_top, - ll_round(floater_close_box_size * mButtonScale), - ll_round(floater_close_box_size * mButtonScale)); - } - - LLButton::Params p; - p.name(sButtonNames[i]); - p.rect(btn_rect); - p.image_unselected = getButtonImage(floater_params, (EFloaterButton)i); - // Selected, no matter if hovered or not, is "pressed" - LLUIImage* pressed_image = getButtonPressedImage(floater_params, (EFloaterButton)i); - p.image_selected = pressed_image; - p.image_hover_selected = pressed_image; - // Use a glow effect when the user hovers over the button - // These icons are really small, need glow amount increased - p.hover_glow_amount( 0.33f ); - p.click_callback.function(boost::bind(sButtonCallbacks[i], this)); - p.tab_stop(false); - p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT); - p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome()); - p.scale_image(true); - p.chrome(true); - - LLButton* buttonp = LLUICtrlFactory::create(p); - addChild(buttonp); - mButtons[i] = buttonp; - } - - updateTitleButtons(); -} - -// static -LLUIImage* LLFloater::getButtonImage(const Params& p, EFloaterButton e) -{ - switch(e) - { - default: - case BUTTON_CLOSE: - return p.close_image; - case BUTTON_RESTORE: - return p.restore_image; - case BUTTON_MINIMIZE: - return p.minimize_image; - case BUTTON_TEAR_OFF: - return p.tear_off_image; - case BUTTON_DOCK: - return p.dock_image; - case BUTTON_HELP: - return p.help_image; - } -} - -// static -LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e) -{ - switch(e) - { - default: - case BUTTON_CLOSE: - return p.close_pressed_image; - case BUTTON_RESTORE: - return p.restore_pressed_image; - case BUTTON_MINIMIZE: - return p.minimize_pressed_image; - case BUTTON_TEAR_OFF: - return p.tear_off_pressed_image; - case BUTTON_DOCK: - return p.dock_pressed_image; - case BUTTON_HELP: - return p.help_pressed_image; - } -} - -// static -std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome) -{ - // EXT-4081 (Lag Meter: Ctrl+W does not close floater) - // If floater is chrome set 'Close' text for close button's tooltip - if(is_chrome && BUTTON_CLOSE == e) - { - static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME"); - return close_tooltip_chrome; - } - // TODO: per-floater localizable tooltips set in XML - return sButtonToolTips[e]; -} - -///////////////////////////////////////////////////// -// LLFloaterView - -static LLDefaultChildRegistry::Register r("floater_view"); - -LLFloaterView::LLFloaterView (const Params& p) -: LLUICtrl (p), - mFocusCycleMode(false), - mMinimizePositionVOffset(0), - mSnapOffsetBottom(0), - mSnapOffsetRight(0) -{ - mSnapView = getHandle(); -} - -// By default, adjust vertical. -void LLFloaterView::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLView::reshape(width, height, called_from_parent); - - mLastSnapRect = getSnapRect(); - - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - LLFloater* floaterp = dynamic_cast(viewp); - if (floaterp->isDependent()) - { - // dependents are moved with their "dependee" - continue; - } - - if (!floaterp->isMinimized() && floaterp->getCanDrag()) - { - LLRect old_rect = floaterp->getRect(); - floaterp->applyPositioning(NULL, false); - LLRect new_rect = floaterp->getRect(); - - //LLRect r = floaterp->getRect(); - - //// Compute absolute distance from each edge of screen - //S32 left_offset = llabs(r.mLeft - 0); - //S32 right_offset = llabs(old_right - r.mRight); - - //S32 top_offset = llabs(old_top - r.mTop); - //S32 bottom_offset = llabs(r.mBottom - 0); - - S32 translate_x = new_rect.mLeft - old_rect.mLeft; - S32 translate_y = new_rect.mBottom - old_rect.mBottom; - - //if (left_offset > right_offset) - //{ - // translate_x = new_right - old_right; - //} - - //if (top_offset < bottom_offset) - //{ - // translate_y = new_top - old_top; - //} - - // don't reposition immovable floaters - //if (floaterp->getCanDrag()) - //{ - // floaterp->translate(translate_x, translate_y); - //} - for (LLHandle dependent_floater : floaterp->mDependents) - { - if (dependent_floater.get()) - { - dependent_floater.get()->translate(translate_x, translate_y); - } - } - } - } -} - - -void LLFloaterView::restoreAll() -{ - // make sure all subwindows aren't minimized - child_list_t child_list = *(getChildList()); - for (LLView* child : child_list) - { - LLFloater* floaterp = dynamic_cast(child); - if (floaterp) - { - floaterp->setMinimized(false); - } - } - - // *FIX: make sure dependents are restored - - // children then deleted by default view constructor -} - - -LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor ) -{ - LLRect base_rect = reference_floater->getRect(); - LLRect::tCoordType width = neighbor->getRect().getWidth(); - LLRect::tCoordType height = neighbor->getRect().getHeight(); - LLRect new_rect = neighbor->getRect(); - - LLRect expanded_base_rect = base_rect; - expanded_base_rect.stretch(10); - for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin(); - dependent_it != reference_floater->mDependents.end(); ++dependent_it) - { - LLFloater* sibling = dependent_it->get(); - // check for dependents within 10 pixels of base floater - if (sibling && - sibling != neighbor && - sibling->getVisible() && - expanded_base_rect.overlaps(sibling->getRect())) - { - base_rect.unionWith(sibling->getRect()); - } - } - - LLRect::tCoordType left_margin = llmax(0, base_rect.mLeft); - LLRect::tCoordType right_margin = llmax(0, getRect().getWidth() - base_rect.mRight); - LLRect::tCoordType top_margin = llmax(0, getRect().getHeight() - base_rect.mTop); - LLRect::tCoordType bottom_margin = llmax(0, base_rect.mBottom); - - // find position for floater in following order - // right->left->bottom->top - for (S32 i = 0; i < 5; i++) - { - if (right_margin > width) - { - new_rect.translate(base_rect.mRight - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mTop); - return new_rect; - } - else if (left_margin > width) - { - new_rect.translate(base_rect.mLeft - neighbor->getRect().mRight, base_rect.mTop - neighbor->getRect().mTop); - return new_rect; - } - else if (bottom_margin > height) - { - new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mBottom - neighbor->getRect().mTop); - return new_rect; - } - else if (top_margin > height) - { - new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mBottom); - return new_rect; - } - - // keep growing margins to find "best" fit - left_margin += 20; - right_margin += 20; - top_margin += 20; - bottom_margin += 20; - } - - // didn't find anything, return initial rect - return new_rect; -} - - -void LLFloaterView::bringToFront(LLFloater* child, bool give_focus, bool restore) -{ - if (!child) - return; - - LLFloater* front_child = mFrontChildHandle.get(); - if (front_child == child) - { - if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child)) - { - child->setFocus(true); - } - return; - } - - if (front_child && front_child->getVisible()) - { - front_child->goneFromFront(); - } - - mFrontChildHandle = child->getHandle(); - - // *TODO: make this respect floater's mAutoFocus value, instead of - // using parameter - if (child->getHost()) - { - // this floater is hosted elsewhere and hence not one of our children, abort - return; - } - std::vector floaters_to_move; - // Look at all floaters...tab - for (child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it) - { - LLFloater* floater = dynamic_cast(*child_it); - - // ...but if I'm a dependent floater... - if (floater && child->isDependent()) - { - // ...look for floaters that have me as a dependent... - LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle()); - - if (found_dependent != floater->mDependents.end()) - { - // ...and make sure all children of that floater (including me) are brought to front... - for (LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); - dependent_it != floater->mDependents.end(); ++dependent_it) - { - LLFloater* sibling = dependent_it->get(); - if (sibling) - { - floaters_to_move.push_back(sibling); - } - } - //...before bringing my parent to the front... - floaters_to_move.push_back(floater); - } - } - } - - std::vector::iterator floater_it; - for(floater_it = floaters_to_move.begin(); floater_it != floaters_to_move.end(); ++floater_it) - { - LLFloater* floaterp = *floater_it; - sendChildToFront(floaterp); - - // always unminimize dependee, but allow dependents to stay minimized - if (!floaterp->isDependent()) - { - floaterp->setMinimized(false); - } - } - floaters_to_move.clear(); - - // ...then bringing my own dependents to the front... - for (LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin(); - dependent_it != child->mDependents.end(); ++dependent_it) - { - LLFloater* dependent = dependent_it->get(); - if (dependent) - { - sendChildToFront(dependent); - } - } - - // ...and finally bringing myself to front - // (do this last, so that I'm left in front at end of this call) - if (*beginChild() != child) - { - sendChildToFront(child); - } - - if(restore) - { - child->setMinimized(false); - } - - if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) - { - child->setFocus(true); - // floater did not take focus, so relinquish focus to world - if (!child->hasFocus()) - { - gFocusMgr.setKeyboardFocus(NULL); - } - } -} - -void LLFloaterView::highlightFocusedFloater() -{ - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLFloater *floater = (LLFloater *)(*child_it); - - // skip dependent floaters, as we'll handle them in a batch along with their dependee(?) - if (floater->isDependent()) - { - continue; - } - - bool floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater); - for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); - dependent_it != floater->mDependents.end(); - ++dependent_it) - { - LLFloater* dependent_floaterp = dependent_it->get(); - if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp)) - { - floater_or_dependent_has_focus = true; - } - } - - // now set this floater and all its dependents - floater->setForeground(floater_or_dependent_has_focus); - - for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); - dependent_it != floater->mDependents.end(); ) - { - LLFloater* dependent_floaterp = dependent_it->get(); - if (dependent_floaterp) - { - dependent_floaterp->setForeground(floater_or_dependent_has_focus); - } - ++dependent_it; - } - - floater->cleanupHandles(); - } -} - -LLFloater* LLFloaterView::getFrontmostClosableFloater() -{ - child_list_const_iter_t child_it; - LLFloater* frontmost_floater = NULL; - - for ( child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - frontmost_floater = (LLFloater *)(*child_it); - - if (frontmost_floater->isInVisibleChain() && frontmost_floater->isCloseable()) - { - return frontmost_floater; - } - } - - return NULL; -} - -void LLFloaterView::unhighlightFocusedFloater() -{ - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLFloater *floater = (LLFloater *)(*child_it); - - floater->setForeground(false); - } -} - -void LLFloaterView::focusFrontFloater() -{ - LLFloater* floaterp = getFrontmost(); - if (floaterp) - { - floaterp->setFocus(true); - } -} - -void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom) -{ - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - static LLUICachedControl minimized_width ("UIMinimizedWidth", 0); - LLRect snap_rect_local = getLocalSnapRect(); - snap_rect_local.mTop += mMinimizePositionVOffset; - for(S32 col = snap_rect_local.mLeft; - col < snap_rect_local.getWidth() - minimized_width; - col += minimized_width) - { - for(S32 row = snap_rect_local.mTop - floater_header_size; - row > floater_header_size; - row -= floater_header_size ) //loop rows - { - - bool foundGap = true; - for(child_list_const_iter_t child_it = getChildList()->begin(); - child_it != getChildList()->end(); - ++child_it) //loop floaters - { - // Examine minimized children. - LLFloater* floater = dynamic_cast(*child_it); - if(floater->isMinimized()) - { - LLRect r = floater->getRect(); - if((r.mBottom < (row + floater_header_size)) - && (r.mBottom > (row - floater_header_size)) - && (r.mLeft < (col + minimized_width)) - && (r.mLeft > (col - minimized_width))) - { - // needs the check for off grid. can't drag, - // but window resize makes them off - foundGap = false; - break; - } - } - } //done floaters - if(foundGap) - { - *left = col; - *bottom = row; - return; //done - } - } //done this col - } - - // crude - stack'em all at 0,0 when screen is full of minimized - // floaters. - *left = snap_rect_local.mLeft; - *bottom = snap_rect_local.mBottom; -} - - -void LLFloaterView::destroyAllChildren() -{ - LLView::deleteAllChildren(); -} - -void LLFloaterView::closeAllChildren(bool app_quitting) -{ - // iterate over a copy of the list, because closing windows will destroy - // some windows on the list. - child_list_t child_list = *(getChildList()); - - for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it) - { - LLView* viewp = *it; - child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp); - if (exists == getChildList()->end()) - { - // this floater has already been removed - continue; - } - - LLFloater* floaterp = dynamic_cast(viewp); - - // Attempt to close floater. This will cause the "do you want to save" - // dialogs to appear. - // Skip invisible floaters if we're not quitting (STORM-192). - if (floaterp->canClose() && !floaterp->isDead() && - (app_quitting || floaterp->getVisible())) - { - floaterp->closeFloater(app_quitting); - } - } -} - -void LLFloaterView::hiddenFloaterClosed(LLFloater* floater) -{ - for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end(); - it != end_it; - ++it) - { - if (it->first.get() == floater) - { - it->second.disconnect(); - mHiddenFloaters.erase(it); - break; - } - } -} - -void LLFloaterView::hideAllFloaters() -{ - child_list_t child_list = *(getChildList()); - - for (child_list_iter_t it = child_list.begin(); it != child_list.end(); ++it) - { - LLFloater* floaterp = dynamic_cast(*it); - if (floaterp && floaterp->getVisible()) - { - floaterp->setVisible(false); - boost::signals2::connection connection = floaterp->mCloseSignal.connect(boost::bind(&LLFloaterView::hiddenFloaterClosed, this, floaterp)); - mHiddenFloaters.push_back(std::make_pair(floaterp->getHandle(), connection)); - } - } -} - -void LLFloaterView::showHiddenFloaters() -{ - for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end(); - it != end_it; - ++it) - { - LLFloater* floaterp = it->first.get(); - if (floaterp) - { - floaterp->setVisible(true); - } - it->second.disconnect(); - } - mHiddenFloaters.clear(); -} - -bool LLFloaterView::allChildrenClosed() -{ - // see if there are any visible floaters (some floaters "close" - // by setting themselves invisible) - for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it) - { - LLFloater* floaterp = dynamic_cast(*it); - - if (floaterp->getVisible() && !floaterp->isDead() && floaterp->isCloseable()) - { - return false; - } - } - return true; -} - -void LLFloaterView::shiftFloaters(S32 x_offset, S32 y_offset) -{ - for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it) - { - LLFloater* floaterp = dynamic_cast(*it); - - if (floaterp && floaterp->isMinimized()) - { - floaterp->translate(x_offset, y_offset); - } - } -} - -void LLFloaterView::refresh() -{ - LLRect snap_rect = getSnapRect(); - if (snap_rect != mLastSnapRect) - { - reshape(getRect().getWidth(), getRect().getHeight(), true); - } - - // Constrain children to be entirely on the screen - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLFloater* floaterp = dynamic_cast(*child_it); - if (floaterp && floaterp->getVisible() ) - { - // minimized floaters are kept fully onscreen - adjustToFitScreen(floaterp, !floaterp->isMinimized()); - } - } -} - -void LLFloaterView::adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars/* = false*/) -{ - if (floater->getParent() != this) - { - // floater is hosted elsewhere, so ignore - return; - } - - if (floater->getDependee() && - floater->getDependee() == floater->getSnapTarget().get()) - { - // floater depends on other and snaps to it, so ignore - return; - } - - LLRect::tCoordType screen_width = getSnapRect().getWidth(); - LLRect::tCoordType screen_height = getSnapRect().getHeight(); - - // only automatically resize non-minimized, resizable floaters - if( floater->isResizable() && !floater->isMinimized() ) - { - LLRect view_rect = floater->getRect(); - S32 old_width = view_rect.getWidth(); - S32 old_height = view_rect.getHeight(); - S32 min_width; - S32 min_height; - floater->getResizeLimits( &min_width, &min_height ); - - // Make sure floater isn't already smaller than its min height/width? - S32 new_width = llmax( min_width, old_width ); - S32 new_height = llmax( min_height, old_height); - - if((new_width > screen_width) || (new_height > screen_height)) - { - // We have to make this window able to fit on screen - new_width = llmin(new_width, screen_width); - new_height = llmin(new_height, screen_height); - - // ...while respecting minimum width/height - new_width = llmax(new_width, min_width); - new_height = llmax(new_height, min_height); - - LLRect new_rect; - new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height); - - floater->setShape(new_rect); - - if (floater->followsRight()) - { - floater->translate(old_width - new_width, 0); - } - - if (floater->followsTop()) - { - floater->translate(0, old_height - new_height); - } - } - } - - const LLRect& constraint = snap_in_toolbars ? getSnapRect() : gFloaterView->getRect(); - S32 min_overlap_pixels = allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX; - - floater->fitWithDependentsOnScreen(mToolbarLeftRect, mToolbarBottomRect, mToolbarRightRect, constraint, min_overlap_pixels); -} - -void LLFloaterView::draw() -{ - refresh(); - - // hide focused floater if in cycle mode, so that it can be drawn on top - LLFloater* focused_floater = getFocusedFloater(); - - if (mFocusCycleMode && focused_floater) - { - child_list_const_iter_t child_it = getChildList()->begin(); - for (;child_it != getChildList()->end(); ++child_it) - { - if ((*child_it) != focused_floater) - { - drawChild(*child_it); - } - } - - drawChild(focused_floater, -TABBED_FLOATER_OFFSET, TABBED_FLOATER_OFFSET); - } - else - { - LLView::draw(); - } -} - -LLRect LLFloaterView::getSnapRect() const -{ - LLRect snap_rect = getLocalRect(); - - LLView* snap_view = mSnapView.get(); - if (snap_view) - { - snap_view->localRectToOtherView(snap_view->getLocalRect(), &snap_rect, this); - } - - return snap_rect; -} - -LLFloater *LLFloaterView::getFocusedFloater() const -{ - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - if ((*child_it)->isCtrl()) - { - LLFloater* ctrlp = dynamic_cast(*child_it); - if ( ctrlp && ctrlp->hasFocus() ) - { - return ctrlp; - } - } - } - return NULL; -} - -LLFloater *LLFloaterView::getFrontmost() const -{ - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if ( viewp->getVisible() && !viewp->isDead()) - { - return (LLFloater *)viewp; - } - } - return NULL; -} - -LLFloater *LLFloaterView::getBackmost() const -{ - LLFloater* back_most = NULL; - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if ( viewp->getVisible() ) - { - back_most = (LLFloater *)viewp; - } - } - return back_most; -} - -void LLFloaterView::syncFloaterTabOrder() -{ - LLFloater* front_child = mFrontChildHandle.get(); - if (front_child && front_child->getIsChrome()) - return; - - // look for a visible modal dialog, starting from first - LLModalDialog* modal_dialog = NULL; - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLModalDialog* dialog = dynamic_cast(*child_it); - if (dialog && dialog->isModal() && dialog->getVisible()) - { - modal_dialog = dialog; - break; - } - } - - if (modal_dialog) - { - // If we have a visible modal dialog, make sure that it has focus - LLUI::getInstance()->addPopup(modal_dialog); - - if( !gFocusMgr.childHasKeyboardFocus( modal_dialog ) ) - { - modal_dialog->setFocus(true); - } - - if( !gFocusMgr.childHasMouseCapture( modal_dialog ) ) - { - gFocusMgr.setMouseCapture( modal_dialog ); - } - } - else - { - // otherwise, make sure the focused floater is in the front of the child list - for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it) - { - LLFloater* floaterp = dynamic_cast(*child_it); - if (gFocusMgr.childHasKeyboardFocus(floaterp)) - { - LLFloater* front_child = mFrontChildHandle.get(); - if (front_child != floaterp) - { - // Grab a list of the top floaters that want to stay on top of the focused floater - std::list listTop; - if (front_child && !front_child->canFocusStealFrontmost()) - { - for (LLView* childp : *getChildList()) - { - LLFloater* child_floaterp = static_cast(childp); - if (child_floaterp->canFocusStealFrontmost()) - break; - listTop.push_back(child_floaterp); - } - } - - bringToFront(floaterp, false); - - // Restore top floaters - if (!listTop.empty()) - { - for (LLView* childp : listTop) - { - sendChildToFront(childp); - } - mFrontChildHandle = listTop.back()->getHandle(); - } - } - - break; - } - } - } -} - -LLFloater* LLFloaterView::getParentFloater(LLView* viewp) const -{ - LLView* parentp = viewp->getParent(); - - while(parentp && parentp != this) - { - viewp = parentp; - parentp = parentp->getParent(); - } - - if (parentp == this) - { - return dynamic_cast(viewp); - } - - return NULL; -} - -S32 LLFloaterView::getZOrder(LLFloater* child) -{ - S32 rv = 0; - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if(viewp == child) - { - break; - } - ++rv; - } - return rv; -} - -void LLFloaterView::pushVisibleAll(bool visible, const skip_list_t& skip_list) -{ - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) - { - LLView *view = *child_iter; - if (skip_list.find(view) == skip_list.end()) - { - view->pushVisible(visible); - } - } - - LLFloaterReg::blockShowFloaters(true); -} - -void LLFloaterView::popVisibleAll(const skip_list_t& skip_list) -{ - // 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()) - { - view->popVisible(); - } - } - - LLFloaterReg::blockShowFloaters(false); -} - -void LLFloaterView::setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect) -{ - switch (tb) - { - case LLToolBarEnums::TOOLBAR_LEFT: - mToolbarLeftRect = toolbar_rect; - break; - case LLToolBarEnums::TOOLBAR_BOTTOM: - mToolbarBottomRect = toolbar_rect; - break; - case LLToolBarEnums::TOOLBAR_RIGHT: - mToolbarRightRect = toolbar_rect; - break; - default: - LL_WARNS() << "setToolbarRect() passed odd toolbar number " << (S32) tb << LL_ENDL; - break; - } -} - -void LLFloater::setInstanceName(const std::string& name) -{ - if (name != mInstanceName) - { - llassert_always(mInstanceName.empty()); - mInstanceName = name; - if (!mInstanceName.empty()) - { - std::string ctrl_name = getControlName(mInstanceName, mKey); - initRectControl(); - if (!mVisibilityControl.empty()) - { - mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name); - } - if(!mDocStateControl.empty()) - { - mDocStateControl = LLFloaterReg::declareDockStateControl(ctrl_name); - } - } -} -} - -void LLFloater::setKey(const LLSD& newkey) -{ - // Note: We don't have to do anything special with registration when we change keys - mKey = newkey; -} - -//static -void LLFloater::setupParamsForExport(Params& p, LLView* parent) -{ - // Do rectangle munging to topleft layout first - LLPanel::setupParamsForExport(p, parent); - - // Copy the rectangle out to apply layout constraints - LLRect rect = p.rect; - - // Null out other settings - p.rect.left.setProvided(false); - p.rect.top.setProvided(false); - p.rect.right.setProvided(false); - p.rect.bottom.setProvided(false); - - // Explicitly set width/height - p.rect.width.set( rect.getWidth(), true ); - p.rect.height.set( rect.getHeight(), true ); - - // If you can't resize this floater, don't export min_height - // and min_width - bool can_resize = p.can_resize; - if (!can_resize) - { - p.min_height.setProvided(false); - p.min_width.setProvided(false); - } -} - -void LLFloater::initFromParams(const LLFloater::Params& p) -{ - // *NOTE: We have too many classes derived from LLFloater to retrofit them - // all to pass in params via constructors. So we use this method. - - // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible - LLPanel::initFromParams(p); - - // override any follows flags - if (mPositioning != LLFloaterEnums::POSITIONING_SPECIFIED) - { - setFollows(FOLLOWS_NONE); - } - - mTitle = p.title; - mShortTitle = p.short_title; - applyTitle(); - - setCanTearOff(p.can_tear_off); - setCanMinimize(p.can_minimize); - setCanClose(p.can_close); - setCanDock(p.can_dock); - setCanResize(p.can_resize); - setResizeLimits(p.min_width, p.min_height); - - mDragOnLeft = p.can_drag_on_left; - mHeaderHeight = p.header_height; - mLegacyHeaderHeight = p.legacy_header_height; - mSingleInstance = p.single_instance; - mReuseInstance = p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance; - - mDefaultRelativeX = p.rel_x; - mDefaultRelativeY = p.rel_y; - - mPositioning = p.positioning; - mAutoClose = p.auto_close; - - mSaveRect = p.save_rect; - if (p.save_visibility) - { - mVisibilityControl = "t"; // flag to build mVisibilityControl name once mInstanceName is set - } - if(p.save_dock_state) - { - mDocStateControl = "t"; // flag to build mDocStateControl name once mInstanceName is set - } - - // open callback - if (p.open_callback.isProvided()) - { - setOpenCallback(initCommitCallback(p.open_callback)); - } - // close callback - if (p.close_callback.isProvided()) - { - setCloseCallback(initCommitCallback(p.close_callback)); - } - - if (mDragHandle) - { - mDragHandle->setTitleVisible(p.show_title); - } -} - -boost::signals2::connection LLFloater::setMinimizeCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mMinimizeSignal) mMinimizeSignal = new commit_signal_t(); - return mMinimizeSignal->connect(cb); -} - -boost::signals2::connection LLFloater::setOpenCallback( const commit_signal_t::slot_type& cb ) -{ - return mOpenSignal.connect(cb); -} - -boost::signals2::connection LLFloater::setCloseCallback( const commit_signal_t::slot_type& cb ) -{ - return mCloseSignal.connect(cb); -} - -bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node) -{ - LL_PROFILE_ZONE_SCOPED; - Params default_params(LLUICtrlFactory::getDefaultParams()); - Params params(default_params); - - LLXUIParser parser; - parser.readXUI(node, params, filename); // *TODO: Error checking - - std::string xml_filename = params.filename; - - if (!xml_filename.empty()) - { - LLXMLNodePtr referenced_xml; - - if (output_node) - { - //if we are exporting, we want to export the current xml - //not the referenced xml - Params output_params; - parser.readXUI(node, output_params, LLUICtrlFactory::getInstance()->getCurFileName()); - setupParamsForExport(output_params, parent); - output_node->setName(node->getName()->mString); - parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); - return true; - } - - LLUICtrlFactory::instance().pushFileName(xml_filename); - - if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml)) - { - LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL; - - return false; - } - - Params referenced_params; - parser.readXUI(referenced_xml, referenced_params, LLUICtrlFactory::getInstance()->getCurFileName()); - params.fillFrom(referenced_params); - - // add children using dimensions from referenced xml for consistent layout - setShape(params.rect); - LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance()); - - LLUICtrlFactory::instance().popFileName(); - } - - - if (output_node) - { - Params output_params(params); - setupParamsForExport(output_params, parent); - output_node->setName(node->getName()->mString); - parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); - } - - // Default floater position to top-left corner of screen - // However, some legacy floaters have explicit top or bottom - // coordinates set, so respect their wishes. - if (!params.rect.top.isProvided() && !params.rect.bottom.isProvided()) - { - params.rect.top.set(0); - } - if (!params.rect.left.isProvided() && !params.rect.right.isProvided()) - { - params.rect.left.set(0); - } - params.from_xui = true; - applyXUILayout(params, parent, parent == gFloaterView ? gFloaterView->getSnapRect() : parent->getLocalRect()); - initFromParams(params); - - initFloater(params); - - LLMultiFloater* last_host = LLFloater::getFloaterHost(); - if (node->hasName("multi_floater")) - { - LLFloater::setFloaterHost((LLMultiFloater*) this); - } - - LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node); - - if (node->hasName("multi_floater")) - { - LLFloater::setFloaterHost(last_host); - } - - // HACK: When we changed the header height to 25 pixels in Viewer 2, rather - // than re-layout all the floaters we use this value in pixels to make the - // whole floater bigger and change the top-left coordinate for widgets. - // The goal is to eventually set mLegacyHeaderHeight to zero, which would - // make the top-left corner for widget layout the same as the top-left - // corner of the window's content area. James - S32 header_stretch = (mHeaderHeight - mLegacyHeaderHeight); - if (header_stretch > 0) - { - // Stretch the floater vertically, don't move widgets - LLRect rect = getRect(); - rect.mTop += header_stretch; - - // This will also update drag handle, title bar, close box, etc. - setRect(rect); - } - - bool result; - result = postBuild(); - - if (!result) - { - LL_ERRS() << "Failed to construct floater " << getName() << LL_ENDL; - } - - applyRectControl(); // If we have a saved rect control, apply it - gFloaterView->adjustToFitScreen(this, false); // Floaters loaded from XML should all fit on screen - - moveResizeHandlesToFront(); - - applyDockState(); - - return true; // *TODO: Error checking -} - -bool LLFloater::isShown() const -{ - return ! isMinimized() && isInVisibleChain(); -} - -bool LLFloater::isDetachedAndNotMinimized() -{ - return !getHost() && !isMinimized(); -} - -/* static */ -bool LLFloater::isShown(const LLFloater* floater) -{ - return floater && floater->isShown(); -} - -/* static */ -bool LLFloater::isMinimized(const LLFloater* floater) -{ - return floater && floater->isMinimized(); -} - -/* static */ -bool LLFloater::isVisible(const LLFloater* floater) -{ - return floater && floater->getVisible(); -} - -bool LLFloater::buildFromFile(const std::string& filename) -{ - LL_PROFILE_ZONE_SCOPED; - LLXMLNodePtr root; - - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) - { - LL_WARNS() << "Couldn't find (or parse) floater from: " << filename << LL_ENDL; - return false; - } - - // root must be called floater - if( !(root->hasName("floater") || root->hasName("multi_floater")) ) - { - LL_WARNS() << "Root node should be named floater in: " << filename << LL_ENDL; - return false; - } - - bool res = true; - - LL_DEBUGS() << "Building floater " << filename << LL_ENDL; - LLUICtrlFactory::instance().pushFileName(filename); - { - if (!getFactoryMap().empty()) - { - LLPanel::sFactoryStack.push_front(&getFactoryMap()); - } - - // for local registry callbacks; define in constructor, referenced in XUI or postBuild - getCommitCallbackRegistrar().pushScope(); - getEnableCallbackRegistrar().pushScope(); - - res = initFloaterXML(root, getParent(), filename, NULL); - - setXMLFilename(filename); - - getCommitCallbackRegistrar().popScope(); - getEnableCallbackRegistrar().popScope(); - - if (!getFactoryMap().empty()) - { - LLPanel::sFactoryStack.pop_front(); - } - } - LLUICtrlFactory::instance().popFileName(); - - return res; -} - -void LLFloater::stackWith(LLFloater& other) -{ - static LLUICachedControl floater_offset ("UIFloaterOffset", 16); - - LLRect next_rect; - if (other.getHost()) - { - next_rect = other.getHost()->getRect(); - } - else - { - next_rect = other.getRect(); - } - next_rect.translate(floater_offset, -floater_offset); - - const LLRect& rect = getControlGroup()->getRect(mRectControl); - if (rect.notEmpty() && !mDefaultRectForGroup && mResizable) - { - next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight())); - } - else - { - next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight()); - } - setShape(next_rect); - - if (!other.getHost()) - { - other.mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP; - other.setFollows(FOLLOWS_LEFT | FOLLOWS_TOP); - } -} - -void LLFloater::applyRelativePosition() -{ - LLRect snap_rect = gFloaterView->getSnapRect(); - LLRect floater_view_screen_rect = gFloaterView->calcScreenRect(); - snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom); - LLRect floater_screen_rect = calcScreenRect(); - - LLCoordGL new_center = mPosition.convert(); - LLCoordGL cur_center(floater_screen_rect.getCenterX(), floater_screen_rect.getCenterY()); - translate(new_center.mX - cur_center.mX, new_center.mY - cur_center.mY); -} - - -LLCoordFloater::LLCoordFloater(F32 x, F32 y, LLFloater& floater) -: coord_t((S32)x, (S32)y) -{ - mFloater = floater.getHandle(); -} - - -LLCoordFloater::LLCoordFloater(const LLCoordCommon& other, LLFloater& floater) -{ - mFloater = floater.getHandle(); - convertFromCommon(other); -} - -LLCoordFloater& LLCoordFloater::operator=(const LLCoordFloater& other) -{ - mFloater = other.mFloater; - coord_t::operator =(other); - return *this; -} - -void LLCoordFloater::setFloater(LLFloater& floater) -{ - mFloater = floater.getHandle(); -} - -bool LLCoordFloater::operator==(const LLCoordFloater& other) const -{ - return mX == other.mX && mY == other.mY && mFloater == other.mFloater; -} - -LLCoordCommon LL_COORD_FLOATER::convertToCommon() const -{ - const LLCoordFloater& self = static_cast(LLCoordFloater::getTypedCoords(*this)); - - LLRect snap_rect = gFloaterView->getSnapRect(); - LLRect floater_view_screen_rect = gFloaterView->calcScreenRect(); - snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom); - - LLFloater* floaterp = mFloater.get(); - S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0; - S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0; - LLCoordCommon out; - if (self.mX < -0.5f) - { - out.mX = ll_round(rescale(self.mX, -1.f, -0.5f, snap_rect.mLeft - (floater_width - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mLeft)); - } - else if (self.mX > 0.5f) - { - out.mX = ll_round(rescale(self.mX, 0.5f, 1.f, snap_rect.mRight - floater_width, snap_rect.mRight - FLOATER_MIN_VISIBLE_PIXELS)); - } - else - { - out.mX = ll_round(rescale(self.mX, -0.5f, 0.5f, snap_rect.mLeft, snap_rect.mRight - floater_width)); - } - - if (self.mY < -0.5f) - { - out.mY = ll_round(rescale(self.mY, -1.f, -0.5f, snap_rect.mBottom - (floater_height - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mBottom)); - } - else if (self.mY > 0.5f) - { - out.mY = ll_round(rescale(self.mY, 0.5f, 1.f, snap_rect.mTop - floater_height, snap_rect.mTop - FLOATER_MIN_VISIBLE_PIXELS)); - } - else - { - out.mY = ll_round(rescale(self.mY, -0.5f, 0.5f, snap_rect.mBottom, snap_rect.mTop - floater_height)); - } - - // return center point instead of lower left - out.mX += floater_width / 2; - out.mY += floater_height / 2; - - return out; -} - -void LL_COORD_FLOATER::convertFromCommon(const LLCoordCommon& from) -{ - LLCoordFloater& self = static_cast(LLCoordFloater::getTypedCoords(*this)); - LLRect snap_rect = gFloaterView->getSnapRect(); - LLRect floater_view_screen_rect = gFloaterView->calcScreenRect(); - snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom); - - - LLFloater* floaterp = mFloater.get(); - S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0; - S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0; - - S32 from_x = from.mX - floater_width / 2; - S32 from_y = from.mY - floater_height / 2; - - if (from_x < snap_rect.mLeft) - { - self.mX = rescale(from_x, snap_rect.mLeft - (floater_width - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mLeft, -1.f, -0.5f); - } - else if (from_x + floater_width > snap_rect.mRight) - { - self.mX = rescale(from_x, snap_rect.mRight - floater_width, snap_rect.mRight - FLOATER_MIN_VISIBLE_PIXELS, 0.5f, 1.f); - } - else - { - self.mX = rescale(from_x, snap_rect.mLeft, snap_rect.mRight - floater_width, -0.5f, 0.5f); - } - - if (from_y < snap_rect.mBottom) - { - self.mY = rescale(from_y, snap_rect.mBottom - (floater_height - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mBottom, -1.f, -0.5f); - } - else if (from_y + floater_height > snap_rect.mTop) - { - self.mY = rescale(from_y, snap_rect.mTop - floater_height, snap_rect.mTop - FLOATER_MIN_VISIBLE_PIXELS, 0.5f, 1.f); - } - else - { - self.mY = rescale(from_y, snap_rect.mBottom, snap_rect.mTop - floater_height, -0.5f, 0.5f); - } -} +/** + * @file llfloater.cpp + * @brief LLFloater base class + * + * $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$ + */ + +// Floating "windows" within the GL display, like the inventory floater, +// mini-map floater, etc. + +#include "linden_common.h" +#include "llviewereventrecorder.h" +#include "llfloater.h" + +#include "llfocusmgr.h" + +#include "lluictrlfactory.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llcriticaldamp.h" // LLSmoothInterpolation +#include "lldir.h" +#include "lldraghandle.h" +#include "llfloaterreg.h" +#include "llfocusmgr.h" +#include "llresizebar.h" +#include "llresizehandle.h" +#include "llkeyboard.h" +#include "llmenugl.h" // MENU_BAR_HEIGHT +#include "llmodaldialog.h" +#include "lltextbox.h" +#include "llresmgr.h" +#include "llui.h" +#include "llwindow.h" +#include "llstl.h" +#include "llcontrol.h" +#include "lltabcontainer.h" +#include "v2math.h" +#include "lltrans.h" +#include "llhelp.h" +#include "llmultifloater.h" +#include "llsdutil.h" +#include "lluiusage.h" + + +// use this to control "jumping" behavior when Ctrl-Tabbing +const S32 TABBED_FLOATER_OFFSET = 0; + +const F32 LLFloater::CONTEXT_CONE_IN_ALPHA = 0.0f; +const F32 LLFloater::CONTEXT_CONE_OUT_ALPHA = 1.f; +const F32 LLFloater::CONTEXT_CONE_FADE_TIME = 0.08f; + +namespace LLInitParam +{ + void TypeValues::declareValues() + { + declare("relative", LLFloaterEnums::POSITIONING_RELATIVE); + declare("cascading", LLFloaterEnums::POSITIONING_CASCADING); + declare("centered", LLFloaterEnums::POSITIONING_CENTERED); + declare("specified", LLFloaterEnums::POSITIONING_SPECIFIED); + } +} + +std::string LLFloater::sButtonNames[BUTTON_COUNT] = +{ + "llfloater_close_btn", //BUTTON_CLOSE + "llfloater_restore_btn", //BUTTON_RESTORE + "llfloater_minimize_btn", //BUTTON_MINIMIZE + "llfloater_tear_off_btn", //BUTTON_TEAR_OFF + "llfloater_dock_btn", //BUTTON_DOCK + "llfloater_help_btn" //BUTTON_HELP +}; + +std::string LLFloater::sButtonToolTips[BUTTON_COUNT]; + +std::string LLFloater::sButtonToolTipsIndex[BUTTON_COUNT]= +{ +#ifdef LL_DARWIN + "BUTTON_CLOSE_DARWIN", //"Close (Cmd-W)", //BUTTON_CLOSE +#else + "BUTTON_CLOSE_WIN", //"Close (Ctrl-W)", //BUTTON_CLOSE +#endif + "BUTTON_RESTORE", //"Restore", //BUTTON_RESTORE + "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE + "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF + "BUTTON_DOCK", + "BUTTON_HELP" +}; + +LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] = +{ + LLFloater::onClickClose, //BUTTON_CLOSE + LLFloater::onClickMinimize, //BUTTON_RESTORE + LLFloater::onClickMinimize, //BUTTON_MINIMIZE + LLFloater::onClickTearOff, //BUTTON_TEAR_OFF + LLFloater::onClickDock, //BUTTON_DOCK + LLFloater::onClickHelp //BUTTON_HELP +}; + +LLMultiFloater* LLFloater::sHostp = NULL; +bool LLFloater::sQuitting = false; // Flag to prevent storing visibility controls while quitting + +LLFloaterView* gFloaterView = NULL; + +/*==========================================================================*| +// DEV-38598: The fundamental problem with this operation is that it can only +// support a subset of LLSD values. While it's plausible to compare two arrays +// lexicographically, what strict ordering can you impose on maps? +// (LLFloaterTOS's current key is an LLSD map.) + +// Of course something like this is necessary if you want to build a std::set +// or std::map with LLSD keys. Fortunately we're getting by with other +// container types for now. + +//static +bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b) +{ + if (a.type() != b.type()) + { + //LL_ERRS() << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << LL_ENDL; + return false; + } + else if (a.isUndefined()) + return false; + else if (a.isInteger()) + return a.asInteger() < b.asInteger(); + else if (a.isReal()) + return a.asReal() < b.asReal(); + else if (a.isString()) + return a.asString() < b.asString(); + else if (a.isUUID()) + return a.asUUID() < b.asUUID(); + else if (a.isDate()) + return a.asDate() < b.asDate(); + else if (a.isURI()) + return a.asString() < b.asString(); // compare URIs as strings + else if (a.isBoolean()) + return a.asBoolean() < b.asBoolean(); + else + return false; // no valid operation for Binary +} +|*==========================================================================*/ + +bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b) +{ + return llsd_equals(a, b); +} + +//************************************ + +LLFloater::Params::Params() +: title("title"), + short_title("short_title"), + single_instance("single_instance", false), + reuse_instance("reuse_instance", false), + can_resize("can_resize", false), + can_minimize("can_minimize", true), + can_close("can_close", true), + can_drag_on_left("can_drag_on_left", false), + can_tear_off("can_tear_off", true), + save_dock_state("save_dock_state", false), + save_rect("save_rect", false), + save_visibility("save_visibility", false), + can_dock("can_dock", false), + show_title("show_title", true), + auto_close("auto_close", false), + positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE), + header_height("header_height", 0), + legacy_header_height("legacy_header_height", 0), + close_image("close_image"), + restore_image("restore_image"), + minimize_image("minimize_image"), + tear_off_image("tear_off_image"), + dock_image("dock_image"), + help_image("help_image"), + close_pressed_image("close_pressed_image"), + restore_pressed_image("restore_pressed_image"), + minimize_pressed_image("minimize_pressed_image"), + tear_off_pressed_image("tear_off_pressed_image"), + dock_pressed_image("dock_pressed_image"), + help_pressed_image("help_pressed_image"), + open_callback("open_callback"), + close_callback("close_callback"), + follows("follows"), + rel_x("rel_x", 0), + rel_y("rel_y", 0) +{ + changeDefault(visible, false); +} + + +//static +const LLFloater::Params& LLFloater::getDefaultParams() +{ + return LLUICtrlFactory::getDefaultParams(); +} + +//static +void LLFloater::initClass() +{ + // translate tooltips for floater buttons + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] ); + } + + LLControlVariable* ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("ActiveFloaterTransparency").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency)); + updateActiveFloaterTransparency(); + } + + ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("InactiveFloaterTransparency").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency)); + updateInactiveFloaterTransparency(); + } + +} + +// defaults for floater param block pulled from widgets/floater.xml +static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater"); + +LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) +: LLPanel(), // intentionally do not pass params here, see initFromParams + mDragHandle(NULL), + mTitle(p.title), + mShortTitle(p.short_title), + mSingleInstance(p.single_instance), + mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default + mKey(key), + mCanTearOff(p.can_tear_off), + mCanMinimize(p.can_minimize), + mCanClose(p.can_close), + mDragOnLeft(p.can_drag_on_left), + mResizable(p.can_resize), + mAutoClose(p.auto_close), + mPositioning(p.positioning), + mMinWidth(p.min_width), + mMinHeight(p.min_height), + mHeaderHeight(p.header_height), + mLegacyHeaderHeight(p.legacy_header_height), + mDefaultRectForGroup(true), + mMinimized(false), + mForeground(false), + mFirstLook(true), + mButtonScale(1.0f), + mAutoFocus(true), // automatically take focus when opened + mCanDock(false), + mDocked(false), + mTornOff(false), + mHasBeenDraggedWhileMinimized(false), + mPreviousMinimizedBottom(0), + mPreviousMinimizedLeft(0), + mDefaultRelativeX(p.rel_x), + mDefaultRelativeY(p.rel_y), + mMinimizeSignal(NULL) +// mNotificationContext(NULL) +{ + mPosition.setFloater(*this); +// mNotificationContext = new LLFloaterNotificationContext(getHandle()); + + // Clicks stop here. + setMouseOpaque(true); + + // Floaters always draw their background, unlike every other panel. + setBackgroundVisible(true); + + // Floaters start not minimized. When minimized, they save their + // prior rectangle to be used on restore. + mExpandedRect.set(0,0,0,0); + + memset(mButtonsEnabled, 0, BUTTON_COUNT * sizeof(bool)); + memset(mButtons, 0, BUTTON_COUNT * sizeof(LLButton*)); + + addDragHandle(); + addResizeCtrls(); + + initFromParams(p); + + initFloater(p); +} + +// Note: Floaters constructed from XML call init() twice! +void LLFloater::initFloater(const Params& p) +{ + // Close button. + if (mCanClose) + { + mButtonsEnabled[BUTTON_CLOSE] = true; + } + + // Help button: '?' + //SL-14050 Disable all Help question marks + mButtonsEnabled[BUTTON_HELP] = false; + + // Minimize button only for top draggers + if ( !mDragOnLeft && mCanMinimize ) + { + mButtonsEnabled[BUTTON_MINIMIZE] = true; + } + + if(mCanDock) + { + mButtonsEnabled[BUTTON_DOCK] = true; + } + + buildButtons(p); + + // Floaters are created in the invisible state + setVisible(false); + + if (!getParent()) + { + gFloaterView->addChild(this); + } +} + +void LLFloater::addDragHandle() +{ + if (!mDragHandle) + { + if (mDragOnLeft) + { + LLDragHandleLeft::Params p; + p.name("drag"); + p.follows.flags(FOLLOWS_ALL); + p.label(mTitle); + mDragHandle = LLUICtrlFactory::create(p); + } + else // drag on top + { + LLDragHandleTop::Params p; + p.name("Drag Handle"); + p.follows.flags(FOLLOWS_ALL); + p.label(mTitle); + mDragHandle = LLUICtrlFactory::create(p); + } + addChild(mDragHandle); + } + layoutDragHandle(); + applyTitle(); +} + +void LLFloater::layoutDragHandle() +{ + static LLUICachedControl floater_close_box_size ("UIFloaterCloseBoxSize", 0); + S32 close_box_size = mCanClose ? floater_close_box_size : 0; + + LLRect rect; + if (mDragOnLeft) + { + rect.setLeftTopAndSize(0, 0, DRAG_HANDLE_WIDTH, getRect().getHeight() - LLPANEL_BORDER_WIDTH - close_box_size); + } + else // drag on top + { + rect = getLocalRect(); + } + mDragHandle->setShape(rect); + updateTitleButtons(); +} + +// static +void LLFloater::updateActiveFloaterTransparency() +{ + static LLCachedControl active_transparency(*LLUI::getInstance()->mSettingGroups["config"], "ActiveFloaterTransparency", 1.f); + sActiveControlTransparency = active_transparency; +} + +// static +void LLFloater::updateInactiveFloaterTransparency() +{ + static LLCachedControl inactive_transparency(*LLUI::getInstance()->mSettingGroups["config"], "InactiveFloaterTransparency", 0.95f); + sInactiveControlTransparency = inactive_transparency; +} + +void LLFloater::addResizeCtrls() +{ + // Resize bars (sides) + LLResizeBar::Params p; + p.name("resizebar_left"); + p.resizing_view(this); + p.min_size(mMinWidth); + p.side(LLResizeBar::LEFT); + mResizeBar[LLResizeBar::LEFT] = LLUICtrlFactory::create(p); + addChild( mResizeBar[LLResizeBar::LEFT] ); + + p.name("resizebar_top"); + p.min_size(mMinHeight); + p.side(LLResizeBar::TOP); + + mResizeBar[LLResizeBar::TOP] = LLUICtrlFactory::create(p); + addChild( mResizeBar[LLResizeBar::TOP] ); + + p.name("resizebar_right"); + p.min_size(mMinWidth); + p.side(LLResizeBar::RIGHT); + mResizeBar[LLResizeBar::RIGHT] = LLUICtrlFactory::create(p); + addChild( mResizeBar[LLResizeBar::RIGHT] ); + + p.name("resizebar_bottom"); + p.min_size(mMinHeight); + p.side(LLResizeBar::BOTTOM); + mResizeBar[LLResizeBar::BOTTOM] = LLUICtrlFactory::create(p); + addChild( mResizeBar[LLResizeBar::BOTTOM] ); + + // Resize handles (corners) + LLResizeHandle::Params handle_p; + // handles must not be mouse-opaque, otherwise they block hover events + // to other buttons like the close box. JC + handle_p.mouse_opaque(false); + handle_p.min_width(mMinWidth); + handle_p.min_height(mMinHeight); + handle_p.corner(LLResizeHandle::RIGHT_BOTTOM); + mResizeHandle[0] = LLUICtrlFactory::create(handle_p); + addChild(mResizeHandle[0]); + + handle_p.corner(LLResizeHandle::RIGHT_TOP); + mResizeHandle[1] = LLUICtrlFactory::create(handle_p); + addChild(mResizeHandle[1]); + + handle_p.corner(LLResizeHandle::LEFT_BOTTOM); + mResizeHandle[2] = LLUICtrlFactory::create(handle_p); + addChild(mResizeHandle[2]); + + handle_p.corner(LLResizeHandle::LEFT_TOP); + mResizeHandle[3] = LLUICtrlFactory::create(handle_p); + addChild(mResizeHandle[3]); + + layoutResizeCtrls(); +} + +void LLFloater::layoutResizeCtrls() +{ + LLRect rect; + + // Resize bars (sides) + const S32 RESIZE_BAR_THICKNESS = 3; + rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0); + mResizeBar[LLResizeBar::LEFT]->setRect(rect); + + rect = LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS); + mResizeBar[LLResizeBar::TOP]->setRect(rect); + + rect = LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0); + mResizeBar[LLResizeBar::RIGHT]->setRect(rect); + + rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0); + mResizeBar[LLResizeBar::BOTTOM]->setRect(rect); + + // Resize handles (corners) + rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0); + mResizeHandle[0]->setRect(rect); + + rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT); + mResizeHandle[1]->setRect(rect); + + rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ); + mResizeHandle[2]->setRect(rect); + + rect = LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT ); + mResizeHandle[3]->setRect(rect); +} + +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); + } +} + +void LLFloater::destroy() +{ + // LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before + // it was deleted via LLMortician::updateClass(). See EXT-8458. + LLFloaterReg::removeInstance(mInstanceName, mKey); + die(); +} + +// virtual +LLFloater::~LLFloater() +{ + if (!isDead()) + { + // If it's dead, instance is supposed to be already removed, and + // in case of single instance we can remove new one by accident + LLFloaterReg::removeInstance(mInstanceName, mKey); + } + + if( gFocusMgr.childHasKeyboardFocus(this)) + { + // Just in case we might still have focus here, release it. + releaseFocus(); + } + + // This is important so that floaters with persistent rects (i.e., those + // created with rect control rather than an LLRect) are restored in their + // correct, non-minimized positions. + setMinimized( false ); + + delete mDragHandle; + for (S32 i = 0; i < 4; i++) + { + delete mResizeBar[i]; + delete mResizeHandle[i]; + } + + setVisible(false); // We're not visible if we're destroyed + storeVisibilityControl(); + storeDockStateControl(); + delete mMinimizeSignal; +} + +void LLFloater::storeRectControl() +{ + if (!mRectControl.empty()) + { + getControlGroup()->setRect( mRectControl, getRect() ); + } + if (!mPosXControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE) + { + getControlGroup()->setF32( mPosXControl, mPosition.mX ); + } + if (!mPosYControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE) + { + getControlGroup()->setF32( mPosYControl, mPosition.mY ); + } +} + +void LLFloater::storeVisibilityControl() +{ + if( !sQuitting && mVisibilityControl.size() > 1 ) + { + getControlGroup()->setBOOL( mVisibilityControl, getVisible() ); + } +} + +void LLFloater::storeDockStateControl() +{ + if( !sQuitting && mDocStateControl.size() > 1 ) + { + getControlGroup()->setBOOL( mDocStateControl, isDocked() ); + } +} + +// static +std::string LLFloater::getControlName(const std::string& name, const LLSD& key) +{ + std::string ctrl_name = name; + + // Add the key to the control name if appropriate. + if (key.isString() && !key.asString().empty()) + { + ctrl_name += "_" + key.asString(); + } + + return ctrl_name; +} + +// static +LLControlGroup* LLFloater::getControlGroup() +{ + // Floater size, position, visibility, etc are saved in per-account settings. + return LLUI::getInstance()->mSettingGroups["account"]; +} + +void LLFloater::setVisible( bool visible ) +{ + LLPanel::setVisible(visible); // calls onVisibilityChange() + if( visible && mFirstLook ) + { + mFirstLook = false; + } + + if( !visible ) + { + LLUI::getInstance()->removePopup(this); + + if( gFocusMgr.childHasMouseCapture( this ) ) + { + gFocusMgr.setMouseCapture(NULL); + } + } + + for(handle_set_iter_t dependent_it = mDependents.begin(); + dependent_it != mDependents.end(); ) + { + LLFloater* floaterp = dependent_it->get(); + + if (floaterp) + { + floaterp->setVisible(visible); + } + ++dependent_it; + } + + storeVisibilityControl(); +} + + +void LLFloater::setIsSingleInstance(bool is_single_instance) +{ + mSingleInstance = is_single_instance; + if (!mIsReuseInitialized) + { + mReuseInstance = is_single_instance; // reuse single-instance floaters by default + } +} + + +// virtual +void LLFloater::onVisibilityChange ( bool new_visibility ) +{ + if (new_visibility) + { + if (getHost()) + getHost()->setFloaterFlashing(this, false); + } + LLPanel::onVisibilityChange ( new_visibility ); +} + +void LLFloater::openFloater(const LLSD& key) +{ + LL_INFOS() << "Opening floater " << getName() << " full path: " << getPathname() << LL_ENDL; + + LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), true,"floater"); // Last param is event subtype or empty string + + mKey = key; // in case we need to open ourselves again + + if (getSoundFlags() != SILENT + // don't play open sound for hosted (tabbed) windows + && !getHost() + && !getFloaterHost() + && (!getVisible() || isMinimized())) + { + make_ui_sound("UISndWindowOpen"); + } + + //RN: for now, we don't allow rehosting from one multifloater to another + // just need to fix the bugs + if (getFloaterHost() != NULL && getHost() == NULL) + { + // needs a host + // only select tabs if window they are hosted in is visible + getFloaterHost()->addFloater(this, getFloaterHost()->getVisible()); + } + + if (getHost() != NULL) + { + getHost()->setMinimized(false); + getHost()->setVisibleAndFrontmost(mAutoFocus && !getIsChrome()); + getHost()->showFloater(this); + } + else + { + LLFloater* floater_to_stack = LLFloaterReg::getLastFloaterInGroup(mInstanceName); + if (!floater_to_stack) + { + floater_to_stack = LLFloaterReg::getLastFloaterCascading(); + } + applyControlsAndPosition(floater_to_stack); + setMinimized(false); + setVisibleAndFrontmost(mAutoFocus && !getIsChrome()); + } + + mOpenSignal(this, key); + onOpen(key); + + dirtyRect(); +} + +void LLFloater::closeFloater(bool app_quitting) +{ + LL_INFOS() << "Closing floater " << getName() << LL_ENDL; + LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), false,"floater"); // Last param is event subtype or empty string + if (app_quitting) + { + LLFloater::sQuitting = true; + } + + // Always unminimize before trying to close. + // Most of the time the user will never see this state. + setMinimized(false); + + if (canClose()) + { + if (getHost()) + { + ((LLMultiFloater*)getHost())->removeFloater(this); + gFloaterView->addChild(this); + } + + if (getSoundFlags() != SILENT + && getVisible() + && !getHost() + && !app_quitting) + { + make_ui_sound("UISndWindowClose"); + } + + gFocusMgr.clearLastFocusForGroup(this); + + if (hasFocus()) + { + // Do this early, so UI controls will commit before the + // window is taken down. + releaseFocus(); + + // give focus to dependee floater if it exists, and we had focus first + if (isDependent()) + { + LLFloater* dependee = mDependeeHandle.get(); + if (dependee && !dependee->isDead()) + { + dependee->setFocus(true); + } + } + } + + + //If floater is a dependent, remove it from parent (dependee) + LLFloater* dependee = mDependeeHandle.get(); + if (dependee) + { + dependee->removeDependentFloater(this); + } + + // now close dependent floater + while(mDependents.size() > 0) + { + handle_set_iter_t dependent_it = mDependents.begin(); + LLFloater* floaterp = dependent_it->get(); + // normally removeDependentFloater will do this, but in + // case floaterp is somehow invalid or orphaned, erase now + mDependents.erase(dependent_it); + if (floaterp) + { + floaterp->mDependeeHandle = LLHandle(); + floaterp->closeFloater(app_quitting); + } + } + + cleanupHandles(); + + dirtyRect(); + + // Close callbacks + onClose(app_quitting); + mCloseSignal(this, LLSD(app_quitting)); + + // Hide or Destroy + if (mSingleInstance) + { + // Hide the instance + if (getHost()) + { + getHost()->setVisible(false); + } + else + { + setVisible(false); + if (!mReuseInstance) + { + destroy(); + } + } + } + else + { + setVisible(false); // hide before destroying (so onVisibilityChange() gets called) + if (!mReuseInstance) + { + destroy(); + } + } + } +} + +/*virtual*/ +void LLFloater::closeHostedFloater() +{ + // When toggling *visibility*, close the host instead of the floater when hosted + if (getHost()) + { + getHost()->closeFloater(); + } + else + { + closeFloater(); + } +} + +/*virtual*/ +void LLFloater::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLPanel::reshape(width, height, called_from_parent); +} + +// virtual +void LLFloater::translate(S32 x, S32 y) +{ + LLView::translate(x, y); + + if (!mTranslateWithDependents || mDependents.empty()) + return; + + for (const LLHandle& handle : mDependents) + { + LLFloater* floater = handle.get(); + if (floater && floater->getSnapTarget() == getHandle()) + { + floater->LLView::translate(x, y); + } + } +} + +void LLFloater::releaseFocus() +{ + LLUI::getInstance()->removePopup(this); + + setFocus(false); + + if( gFocusMgr.childHasMouseCapture( this ) ) + { + gFocusMgr.setMouseCapture(NULL); + } +} + + +void LLFloater::setResizeLimits( S32 min_width, S32 min_height ) +{ + mMinWidth = min_width; + mMinHeight = min_height; + + for( S32 i = 0; i < 4; i++ ) + { + if( mResizeBar[i] ) + { + if (i == LLResizeBar::LEFT || i == LLResizeBar::RIGHT) + { + mResizeBar[i]->setResizeLimits( min_width, S32_MAX ); + } + else + { + mResizeBar[i]->setResizeLimits( min_height, S32_MAX ); + } + } + if( mResizeHandle[i] ) + { + mResizeHandle[i]->setResizeLimits( min_width, min_height ); + } + } +} + + +void LLFloater::center() +{ + if(getHost()) + { + // hosted floaters can't move + return; + } + centerWithin(gFloaterView->getRect()); +} + +LLMultiFloater* LLFloater::getHost() +{ + return (LLMultiFloater*)mHostHandle.get(); +} + +void LLFloater::applyControlsAndPosition(LLFloater* other) +{ + if (!applyDockState()) + { + if (!applyRectControl()) + { + applyPositioning(other, true); + } + } +} + +bool LLFloater::applyRectControl() +{ + bool saved_rect = false; + + LLRect screen_rect = calcScreenRect(); + mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); + + LLFloater* last_in_group = LLFloaterReg::getLastFloaterInGroup(mInstanceName); + if (last_in_group && last_in_group != this) + { + // other floaters in our group, position ourselves relative to them and don't save the rect + if (mDefaultRectForGroup) + { + mRectControl.clear(); + } + mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP; + } + else + { + bool rect_specified = false; + if (!mRectControl.empty()) + { + // If we have a saved rect, use it + const LLRect& rect = getControlGroup()->getRect(mRectControl); + if (rect.notEmpty()) saved_rect = true; + if (saved_rect) + { + setOrigin(rect.mLeft, rect.mBottom); + + if (mResizable) + { + reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight())); + } + mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; + LLRect screen_rect = calcScreenRect(); + mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); + rect_specified = true; + } + } + + LLControlVariablePtr x_control = getControlGroup()->getControl(mPosXControl); + LLControlVariablePtr y_control = getControlGroup()->getControl(mPosYControl); + if (x_control.notNull() + && y_control.notNull() + && !x_control->isDefault() + && !y_control->isDefault()) + { + mPosition.mX = x_control->getValue().asReal(); + mPosition.mY = y_control->getValue().asReal(); + mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; + applyRelativePosition(); + + saved_rect = true; + } + else if ((mDefaultRelativeX != 0) && (mDefaultRelativeY != 0)) + { + mPosition.mX = mDefaultRelativeX; + mPosition.mY = mDefaultRelativeY; + mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; + applyRelativePosition(); + + saved_rect = true; + } + + // remember updated position + if (rect_specified) + { + storeRectControl(); + } + } + + if (saved_rect) + { + // propagate any derived positioning data back to settings file + storeRectControl(); + } + + + return saved_rect; +} + +bool LLFloater::applyDockState() +{ + bool docked = false; + + if (mDocStateControl.size() > 1) + { + docked = getControlGroup()->getBOOL(mDocStateControl); + setDocked(docked); + } + + return docked; +} + +void LLFloater::applyPositioning(LLFloater* other, bool on_open) +{ + // Otherwise position according to the positioning code + switch (mPositioning) + { + case LLFloaterEnums::POSITIONING_CENTERED: + center(); + break; + + case LLFloaterEnums::POSITIONING_SPECIFIED: + break; + + case LLFloaterEnums::POSITIONING_CASCADING: + if (!on_open) + { + applyRelativePosition(); + } + // fall through + case LLFloaterEnums::POSITIONING_CASCADE_GROUP: + if (on_open) + { + if (other != NULL && other != this) + { + stackWith(*other); + } + else + { + static const U32 CASCADING_FLOATER_HOFFSET = 0; + static const U32 CASCADING_FLOATER_VOFFSET = 0; + + const LLRect& snap_rect = gFloaterView->getSnapRect(); + + const S32 horizontal_offset = CASCADING_FLOATER_HOFFSET; + const S32 vertical_offset = snap_rect.getHeight() - CASCADING_FLOATER_VOFFSET; + + S32 rect_height = getRect().getHeight(); + setOrigin(horizontal_offset, vertical_offset - rect_height); + + translate(snap_rect.mLeft, snap_rect.mBottom); + } + setFollows(FOLLOWS_TOP | FOLLOWS_LEFT); + } + break; + + case LLFloaterEnums::POSITIONING_RELATIVE: + { + applyRelativePosition(); + + break; + } + default: + // Do nothing + break; + } +} + +void LLFloater::applyTitle() +{ + if (!mDragHandle) + { + return; + } + + if (isMinimized() && !mShortTitle.empty()) + { + mDragHandle->setTitle( mShortTitle ); + } + else + { + mDragHandle->setTitle ( mTitle ); + } + + if (getHost()) + { + getHost()->updateFloaterTitle(this); + } +} + +std::string LLFloater::getCurrentTitle() const +{ + return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null; +} + +void LLFloater::setTitle( const std::string& title ) +{ + mTitle = title; + applyTitle(); +} + +std::string LLFloater::getTitle() const +{ + if (mTitle.empty()) + { + return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null; + } + else + { + return mTitle; + } +} + +void LLFloater::setShortTitle( const std::string& short_title ) +{ + mShortTitle = short_title; + applyTitle(); +} + +std::string LLFloater::getShortTitle() const +{ + if (mShortTitle.empty()) + { + return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null; + } + else + { + return mShortTitle; + } +} + +bool LLFloater::canSnapTo(const LLView* other_view) +{ + if (NULL == other_view) + { + LL_WARNS() << "other_view is NULL" << LL_ENDL; + return false; + } + + if (other_view != getParent()) + { + const LLFloater* other_floaterp = dynamic_cast(other_view); + if (other_floaterp + && other_floaterp->getSnapTarget() == getHandle() + && mDependents.find(other_floaterp->getHandle()) != mDependents.end()) + { + // this is a dependent that is already snapped to us, so don't snap back to it + return false; + } + } + + return LLPanel::canSnapTo(other_view); +} + +void LLFloater::setSnappedTo(const LLView* snap_view) +{ + if (!snap_view || snap_view == getParent()) + { + clearSnapTarget(); + } + else + { + //RN: assume it's a floater as it must be a sibling to our parent floater + const LLFloater* floaterp = dynamic_cast(snap_view); + if (floaterp) + { + setSnapTarget(floaterp->getHandle()); + } + } +} + +void LLFloater::handleReshape(const LLRect& new_rect, bool by_user) +{ + const LLRect old_rect = getRect(); + LLView::handleReshape(new_rect, by_user); + + if (by_user && !getHost()) + { + LLFloaterView * floaterVp = dynamic_cast(getParent()); + if (floaterVp) + { + floaterVp->adjustToFitScreen(this, !isMinimized()); + } + } + + // if not minimized, adjust all snapped dependents to new shape + if (!isMinimized()) + { + if (by_user) + { + if (isDocked()) + { + setDocked( false, false); + } + mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; + LLRect screen_rect = calcScreenRect(); + mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); + } + storeRectControl(); + + // gather all snapped dependents + for(handle_set_iter_t dependent_it = mDependents.begin(); + dependent_it != mDependents.end(); ++dependent_it) + { + LLFloater* floaterp = dependent_it->get(); + // is a dependent snapped to us? + if (floaterp && floaterp->getSnapTarget() == getHandle()) + { + S32 delta_x = 0; + S32 delta_y = 0; + // check to see if it snapped to right or top, and move if dependee floater is resizing + LLRect dependent_rect = floaterp->getRect(); + if (dependent_rect.mLeft - getRect().mLeft >= old_rect.getWidth() || // dependent on my right? + dependent_rect.mRight == getRect().mLeft + old_rect.getWidth()) // dependent aligned with my right + { + // was snapped directly onto right side or aligned with it + delta_x += new_rect.getWidth() - old_rect.getWidth(); + } + if (dependent_rect.mBottom - getRect().mBottom >= old_rect.getHeight() || + dependent_rect.mTop == getRect().mBottom + old_rect.getHeight()) + { + // was snapped directly onto top side or aligned with it + delta_y += new_rect.getHeight() - old_rect.getHeight(); + } + + // take translation of dependee floater into account as well + delta_x += new_rect.mLeft - old_rect.mLeft; + delta_y += new_rect.mBottom - old_rect.mBottom; + + dependent_rect.translate(delta_x, delta_y); + floaterp->setShape(dependent_rect, by_user); + } + } + } + else + { + // If minimized, and origin has changed, set + // mHasBeenDraggedWhileMinimized to true + if ((new_rect.mLeft != old_rect.mLeft) || + (new_rect.mBottom != old_rect.mBottom)) + { + mHasBeenDraggedWhileMinimized = true; + } + } +} + +void LLFloater::setMinimized(bool minimize) +{ + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + static LLUICachedControl minimized_width ("UIMinimizedWidth", 0); + + if (minimize == mMinimized) return; + + if (mMinimizeSignal) + { + (*mMinimizeSignal)(this, LLSD(minimize)); + } + + if (minimize) + { + // minimized flag should be turned on before release focus + mMinimized = true; + mExpandedRect = getRect(); + + // If the floater has been dragged while minimized in the + // past, then locate it at its previous minimized location. + // Otherwise, ask the view for a minimize position. + if (mHasBeenDraggedWhileMinimized) + { + setOrigin(mPreviousMinimizedLeft, mPreviousMinimizedBottom); + } + else + { + S32 left, bottom; + gFloaterView->getMinimizePosition(&left, &bottom); + setOrigin( left, bottom ); + } + + if (mButtonsEnabled[BUTTON_MINIMIZE]) + { + mButtonsEnabled[BUTTON_MINIMIZE] = false; + mButtonsEnabled[BUTTON_RESTORE] = true; + } + + setBorderVisible(true); + + for(handle_set_iter_t dependent_it = mDependents.begin(); + dependent_it != mDependents.end(); + ++dependent_it) + { + LLFloater* floaterp = dependent_it->get(); + if (floaterp) + { + if (floaterp->isMinimizeable()) + { + floaterp->setMinimized(true); + } + else if (!floaterp->isMinimized()) + { + floaterp->setVisible(false); + } + } + } + + // Lose keyboard focus when minimized + releaseFocus(); + + for (S32 i = 0; i < 4; i++) + { + if (mResizeBar[i] != NULL) + { + mResizeBar[i]->setEnabled(false); + } + if (mResizeHandle[i] != NULL) + { + mResizeHandle[i]->setEnabled(false); + } + } + + // Reshape *after* setting mMinimized + reshape( minimized_width, floater_header_size, true); + } + else + { + // If this window has been dragged while minimized (at any time), + // remember its position for the next time it's minimized. + if (mHasBeenDraggedWhileMinimized) + { + const LLRect& currentRect = getRect(); + mPreviousMinimizedLeft = currentRect.mLeft; + mPreviousMinimizedBottom = currentRect.mBottom; + } + + setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom ); + if (mButtonsEnabled[BUTTON_RESTORE]) + { + mButtonsEnabled[BUTTON_MINIMIZE] = true; + mButtonsEnabled[BUTTON_RESTORE] = false; + } + + // show dependent floater + for(handle_set_iter_t dependent_it = mDependents.begin(); + dependent_it != mDependents.end(); + ++dependent_it) + { + LLFloater* floaterp = dependent_it->get(); + if (floaterp) + { + floaterp->setMinimized(false); + floaterp->setVisible(true); + } + } + + for (S32 i = 0; i < 4; i++) + { + if (mResizeBar[i] != NULL) + { + mResizeBar[i]->setEnabled(isResizable()); + } + if (mResizeHandle[i] != NULL) + { + mResizeHandle[i]->setEnabled(isResizable()); + } + } + + mMinimized = false; + setFrontmost(); + // Reshape *after* setting mMinimized + reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), true ); + } + + make_ui_sound("UISndWindowClose"); + updateTitleButtons(); + applyTitle (); +} + +void LLFloater::setFocus( bool b ) +{ + if (b && getIsChrome()) + { + return; + } + LLView* last_focus = gFocusMgr.getLastFocusForGroup(this); + // a descendent already has focus + bool child_had_focus = hasFocus(); + + // give focus to first valid descendent + LLPanel::setFocus(b); + + if (b) + { + // only push focused floaters to front of stack if not in midst of ctrl-tab cycle + LLFloaterView * parent = dynamic_cast(getParent()); + if (!getHost() && parent && !parent->getCycleMode()) + { + if (!isFrontmost()) + { + setFrontmost(); + } + } + + // when getting focus, delegate to last descendent which had focus + if (last_focus && !child_had_focus && + last_focus->isInEnabledChain() && + last_focus->isInVisibleChain()) + { + // *FIX: should handle case where focus doesn't stick + last_focus->setFocus(true); + } + } + updateTransparency(b ? TT_ACTIVE : TT_INACTIVE); +} + +// virtual +void LLFloater::setRect(const LLRect &rect) +{ + LLPanel::setRect(rect); + layoutDragHandle(); + layoutResizeCtrls(); +} + +// virtual +void LLFloater::setIsChrome(bool is_chrome) +{ + // chrome floaters don't take focus at all + if (is_chrome) + { + // remove focus if we're changing to chrome + setFocus(false); + // can't Ctrl-Tab to "chrome" floaters + setFocusRoot(false); + mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome))); + } + + LLPanel::setIsChrome(is_chrome); +} + +// Change the draw style to account for the foreground state. +void LLFloater::setForeground(bool front) +{ + if (front != mForeground) + { + mForeground = front; + if (mDragHandle) + mDragHandle->setForeground( front ); + + if (!front) + { + releaseFocus(); + } + + setBackgroundOpaque( front ); + } +} + +void LLFloater::cleanupHandles() +{ + // remove handles to non-existent dependents + for(handle_set_iter_t dependent_it = mDependents.begin(); + dependent_it != mDependents.end(); ) + { + LLFloater* floaterp = dependent_it->get(); + if (!floaterp) + { + dependent_it = mDependents.erase(dependent_it); + } + else + { + ++dependent_it; + } + } +} + +void LLFloater::setHost(LLMultiFloater* host) +{ + if (mHostHandle.isDead() && host) + { + // make buttons smaller for hosted windows to differentiate from parent + mButtonScale = 0.9f; + + // add tear off button + if (mCanTearOff) + { + mButtonsEnabled[BUTTON_TEAR_OFF] = true; + } + } + else if (!mHostHandle.isDead() && !host) + { + mButtonScale = 1.f; + //mButtonsEnabled[BUTTON_TEAR_OFF] = false; + } + if (host) + { + mHostHandle = host->getHandle(); + mLastHostHandle = host->getHandle(); + } + else + { + mHostHandle.markDead(); + } + + updateTitleButtons(); +} + +void LLFloater::moveResizeHandlesToFront() +{ + for( S32 i = 0; i < 4; i++ ) + { + if( mResizeBar[i] ) + { + sendChildToFront(mResizeBar[i]); + } + } + + for( S32 i = 0; i < 4; i++ ) + { + if( mResizeHandle[i] ) + { + sendChildToFront(mResizeHandle[i]); + } + } +} + +/*virtual*/ +bool LLFloater::isFrontmost() +{ + LLFloaterView* floater_view = getParentByType(); + return getVisible() + && (floater_view + && floater_view->getFrontmost() == this); +} + +void LLFloater::addDependentFloater(LLFloater* floaterp, bool reposition, bool resize) +{ + mDependents.insert(floaterp->getHandle()); + floaterp->mDependeeHandle = getHandle(); + + if (reposition) + { + LLRect rect = gFloaterView->findNeighboringPosition(this, floaterp); + if (resize) + { + const LLRect& base = getRect(); + if (rect.mTop == base.mTop) + rect.mBottom = base.mBottom; + else if (rect.mLeft == base.mLeft) + rect.mRight = base.mRight; + floaterp->reshape(rect.getWidth(), rect.getHeight(), false); + } + floaterp->setRect(rect); + floaterp->setSnapTarget(getHandle()); + } + gFloaterView->adjustToFitScreen(floaterp, false, true); + if (floaterp->isFrontmost()) + { + // make sure to bring self and sibling floaters to front + gFloaterView->bringToFront(floaterp, floaterp->getAutoFocus() && !getIsChrome()); + } +} + +void LLFloater::addDependentFloater(LLHandle dependent, bool reposition, bool resize) +{ + LLFloater* dependent_floaterp = dependent.get(); + if(dependent_floaterp) + { + addDependentFloater(dependent_floaterp, reposition, resize); + } +} + +void LLFloater::removeDependentFloater(LLFloater* floaterp) +{ + mDependents.erase(floaterp->getHandle()); + floaterp->mDependeeHandle = LLHandle(); +} + +void LLFloater::fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels) +{ + LLRect total_rect = getRect(); + + for (const LLHandle& handle : mDependents) + { + LLFloater* floater = handle.get(); + if (floater && floater->getSnapTarget() == getHandle()) + { + total_rect.unionWith(floater->getRect()); + } + } + + S32 delta_left = left.notEmpty() ? left.mRight - total_rect.mRight : 0; + S32 delta_bottom = bottom.notEmpty() ? bottom.mTop - total_rect.mTop : 0; + S32 delta_right = right.notEmpty() ? right.mLeft - total_rect.mLeft : 0; + + // move floater with dependings fully onscreen + mTranslateWithDependents = true; + if (translateRectIntoRect(total_rect, constraint, min_overlap_pixels)) + { + clearSnapTarget(); + } + else if (delta_left > 0 && total_rect.mTop < left.mTop && total_rect.mBottom > left.mBottom) + { + translate(delta_left, 0); + } + else if (delta_bottom > 0 && total_rect.mLeft > bottom.mLeft && total_rect.mRight < bottom.mRight) + { + translate(0, delta_bottom); + } + else if (delta_right < 0 && total_rect.mTop < right.mTop && total_rect.mBottom > right.mBottom) + { + translate(delta_right, 0); + } + mTranslateWithDependents = false; +} + +bool LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index) +{ + if( mButtonsEnabled[index] ) + { + LLButton* my_butt = mButtons[index]; + S32 local_x = x - my_butt->getRect().mLeft; + S32 local_y = y - my_butt->getRect().mBottom; + + if ( + my_butt->pointInView(local_x, local_y) && + my_butt->handleMouseDown(local_x, local_y, mask)) + { + // the button handled it + return true; + } + } + return false; +} + +bool LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + LLPanel::handleScrollWheel(x,y,clicks); + return true;//always +} + +// virtual +bool LLFloater::handleMouseUp(S32 x, S32 y, MASK mask) +{ + LL_DEBUGS() << "LLFloater::handleMouseUp calling LLPanel (really LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; + bool handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView + if (handled) { + LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); + } + return handled; +} + +// virtual +bool LLFloater::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if( mMinimized ) + { + // Offer the click to titlebar buttons. + // Note: this block and the offerClickToButton helper method can be removed + // because the parent container will handle it for us but we'll keep it here + // for safety until after reworking the panel code to manage hidden children. + if(offerClickToButton(x, y, mask, BUTTON_CLOSE)) return true; + if(offerClickToButton(x, y, mask, BUTTON_RESTORE)) return true; + if(offerClickToButton(x, y, mask, BUTTON_TEAR_OFF)) return true; + if(offerClickToButton(x, y, mask, BUTTON_DOCK)) return true; + + setFrontmost(true, false); + // Otherwise pass to drag handle for movement + return mDragHandle->handleMouseDown(x, y, mask); + } + else + { + bringToFront( x, y ); + bool handled = LLPanel::handleMouseDown( x, y, mask ); + if (handled) { + LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); + } + return handled; + } +} + +// virtual +bool LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + bool was_minimized = mMinimized; + bringToFront( x, y ); + return was_minimized || LLPanel::handleRightMouseDown( x, y, mask ); +} + +bool LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + bringToFront( x, y ); + return LLPanel::handleMiddleMouseDown( x, y, mask ); +} + + +// virtual +bool LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + bool was_minimized = mMinimized; + setMinimized(false); + return was_minimized || LLPanel::handleDoubleClick(x, y, mask); +} + +// virtual +void LLFloater::bringToFront( S32 x, S32 y ) +{ + if (getVisible() && pointInView(x, y)) + { + LLMultiFloater* hostp = getHost(); + if (hostp) + { + hostp->showFloater(this); + } + else + { + LLFloaterView* parent = dynamic_cast( getParent() ); + if (parent) + { + parent->bringToFront(this, !getIsChrome()); + } + } + } +} + +// virtual +void LLFloater::goneFromFront() +{ + if (mAutoClose) + { + closeFloater(); + } +} + +// virtual +void LLFloater::setVisibleAndFrontmost(bool take_focus,const LLSD& key) +{ + LLUIUsage::instance().logFloater(getInstanceName()); + LLMultiFloater* hostp = getHost(); + if (hostp) + { + hostp->setVisible(true); + hostp->setFrontmost(take_focus); + } + else + { + setVisible(true); + setFrontmost(take_focus); + } +} + +void LLFloater::setFrontmost(bool take_focus, bool restore) +{ + LLMultiFloater* hostp = getHost(); + if (hostp) + { + // this will bring the host floater to the front and select + // the appropriate panel + hostp->showFloater(this); + } + else + { + // there are more than one floater view + // so we need to query our parent directly + LLFloaterView * parent = dynamic_cast( getParent() ); + if (parent) + { + parent->bringToFront(this, take_focus, restore); + } + + // Make sure to set the appropriate transparency type (STORM-732). + updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); + } +} + +void LLFloater::setCanDock(bool b) +{ + if(b != mCanDock) + { + mCanDock = b; + if(mCanDock) + { + mButtonsEnabled[BUTTON_DOCK] = !mDocked; + } + else + { + mButtonsEnabled[BUTTON_DOCK] = false; + } + } + updateTitleButtons(); +} + +void LLFloater::setDocked(bool docked, bool pop_on_undock) +{ + if(docked != mDocked && mCanDock) + { + mDocked = docked; + mButtonsEnabled[BUTTON_DOCK] = !mDocked; + + if (mDocked) + { + setMinimized(false); + mPositioning = LLFloaterEnums::POSITIONING_RELATIVE; + } + + updateTitleButtons(); + + storeDockStateControl(); + } + +} + +// static +void LLFloater::onClickMinimize(LLFloater* self) +{ + if (!self) + return; + self->setMinimized( !self->isMinimized() ); +} + +void LLFloater::onClickTearOff(LLFloater* self) +{ + if (!self) + return; + S32 floater_header_size = self->mHeaderHeight; + LLMultiFloater* host_floater = self->getHost(); + if (host_floater) //Tear off + { + LLRect new_rect; + host_floater->removeFloater(self); + // reparent to floater view + gFloaterView->addChild(self); + + self->openFloater(self->getKey()); + if (self->mSaveRect && !self->mRectControl.empty()) + { + self->applyRectControl(); + } + else + { // only force position for floaters that don't have that data saved + new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight()); + self->setRect(new_rect); + } + gFloaterView->adjustToFitScreen(self, false); + // give focus to new window to keep continuity for the user + self->setFocus(true); + self->setTornOff(true); + } + else //Attach to parent. + { + LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get(); + if (new_host) + { + if (self->mSaveRect) + { + LLRect screen_rect = self->calcScreenRect(); + self->mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert(); + self->storeRectControl(); + } + self->setMinimized(false); // to reenable minimize button if it was minimized + new_host->showFloater(self); + // make sure host is visible + new_host->openFloater(new_host->getKey()); + } + self->setTornOff(false); + } + self->updateTitleButtons(); + self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); +} + +// static +void LLFloater::onClickDock(LLFloater* self) +{ + if(self && self->mCanDock) + { + self->setDocked(!self->mDocked, true); + } +} + +// static +void LLFloater::onClickHelp( LLFloater* self ) +{ + if (self && LLUI::getInstance()->mHelpImpl) + { + // find the current help context for this floater + std::string help_topic; + if (self->findHelpTopic(help_topic)) + { + LLUI::getInstance()->mHelpImpl->showTopic(help_topic); + } + } +} + +void LLFloater::initRectControl() +{ + // save_rect and save_visibility only apply to registered floaters + if (mSaveRect) + { + std::string ctrl_name = getControlName(mInstanceName, mKey); + mRectControl = LLFloaterReg::declareRectControl(ctrl_name); + mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name); + mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name); + } +} + +// static +void LLFloater::closeFrontmostFloater() +{ + LLFloater* floater_to_close = gFloaterView->getFrontmostClosableFloater(); + if(floater_to_close) + { + floater_to_close->closeFloater(); + } + + // if nothing took focus after closing focused floater + // give it to next floater (to allow closing multiple windows via keyboard in rapid succession) + if (gFocusMgr.getKeyboardFocus() == NULL) + { + // HACK: use gFloaterView directly in case we are using Ctrl-W to close snapshot window + // which sits in gSnapshotFloaterView, and needs to pass focus on to normal floater view + gFloaterView->focusFrontFloater(); + } +} + + +// static +void LLFloater::onClickClose( LLFloater* self ) +{ + if (!self) + return; + self->onClickCloseBtn(); +} + +void LLFloater::onClickCloseBtn(bool app_quitting) +{ + closeFloater(false); +} + + +// virtual +void LLFloater::draw() +{ + const F32 alpha = getCurrentTransparency(); + + // draw background + if( isBackgroundVisible() ) + { + drawShadow(this); + + S32 left = LLPANEL_BORDER_WIDTH; + S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH; + S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH; + S32 bottom = LLPANEL_BORDER_WIDTH; + + LLUIImage* image = NULL; + LLColor4 color; + LLColor4 overlay_color; + if (isBackgroundOpaque()) + { + // NOTE: image may not be set + image = getBackgroundImage(); + color = getBackgroundColor(); + overlay_color = getBackgroundImageOverlay(); + } + else + { + image = getTransparentImage(); + color = getTransparentColor(); + overlay_color = getTransparentImageOverlay(); + } + + if (image) + { + // We're using images for this floater's backgrounds + image->draw(getLocalRect(), overlay_color % alpha); + } + else + { + // We're not using images, use old-school flat colors + gl_rect_2d( left, top, right, bottom, color % alpha ); + + // draw highlight on title bar to indicate focus. RDW + if(hasFocus() + && !getIsChrome() + && !getCurrentTitle().empty()) + { + static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor"); + + const LLFontGL* font = LLFontGL::getFontSansSerif(); + LLRect r = getRect(); + gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - font->getLineHeight() - 1, + titlebar_focus_color % alpha, 0, true); + } + } + } + + LLPanel::updateDefaultBtn(); + + if( getDefaultButton() ) + { + if (hasFocus() && getDefaultButton()->getEnabled()) + { + LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); + // is this button a direct descendent and not a nested widget (e.g. checkbox)? + bool focus_is_child_button = dynamic_cast(focus_ctrl) != NULL && dynamic_cast(focus_ctrl)->getParent() == this; + // only enable default button when current focus is not a button + getDefaultButton()->setBorderEnabled(!focus_is_child_button); + } + else + { + getDefaultButton()->setBorderEnabled(false); + } + } + if (isMinimized()) + { + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + drawChild(mButtons[i]); + } + drawChild(mDragHandle, 0, 0, true); + } + else + { + // don't call LLPanel::draw() since we've implemented custom background rendering + LLView::draw(); + } + + // update tearoff button for torn off floaters + // when last host goes away + if (mCanTearOff && !getHost()) + { + LLFloater* old_host = mLastHostHandle.get(); + if (!old_host) + { + setCanTearOff(false); + } + } +} + +void LLFloater::drawShadow(LLPanel* panel) +{ + S32 left = LLPANEL_BORDER_WIDTH; + S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH; + S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH; + S32 bottom = LLPANEL_BORDER_WIDTH; + + static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow"); + LLColor4 shadow_color = shadow_color_cached; + F32 shadow_offset = (F32)DROP_SHADOW_FLOATER; + + if (!panel->isBackgroundOpaque()) + { + shadow_offset *= 0.2f; + shadow_color.mV[VALPHA] *= 0.5f; + } + gl_drop_shadow(left, top, right, bottom, + shadow_color % getCurrentTransparency(), + ll_round(shadow_offset)); +} + +void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type) +{ + if (!view) return; + child_list_t children = *view->getChildList(); + child_list_t::iterator it = children.begin(); + + LLUICtrl* ctrl = dynamic_cast(view); + if (ctrl) + { + ctrl->setTransparencyType(transparency_type); + } + + for(; it != children.end(); ++it) + { + updateTransparency(*it, transparency_type); + } +} + +void LLFloater::updateTransparency(ETypeTransparency transparency_type) +{ + updateTransparency(this, transparency_type); +} + +void LLFloater::setCanMinimize(bool can_minimize) +{ + // if removing minimize/restore button programmatically, + // go ahead and unminimize floater + mCanMinimize = can_minimize; + if (!can_minimize) + { + setMinimized(false); + } + + mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized(); + mButtonsEnabled[BUTTON_RESTORE] = can_minimize && isMinimized(); + + updateTitleButtons(); +} + +void LLFloater::setCanClose(bool can_close) +{ + mCanClose = can_close; + mButtonsEnabled[BUTTON_CLOSE] = can_close; + + updateTitleButtons(); +} + +void LLFloater::setCanTearOff(bool can_tear_off) +{ + mCanTearOff = can_tear_off; + mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead(); + + updateTitleButtons(); +} + + +void LLFloater::setCanResize(bool can_resize) +{ + mResizable = can_resize; + enableResizeCtrls(can_resize); +} + +void LLFloater::setCanDrag(bool can_drag) +{ + // if we delete drag handle, we no longer have access to the floater's title + // so just enable/disable it + if (!can_drag && mDragHandle->getEnabled()) + { + mDragHandle->setEnabled(false); + } + else if (can_drag && !mDragHandle->getEnabled()) + { + mDragHandle->setEnabled(true); + } +} + +bool LLFloater::getCanDrag() +{ + return mDragHandle->getEnabled(); +} + + +void LLFloater::updateTitleButtons() +{ + static LLUICachedControl floater_close_box_size ("UIFloaterCloseBoxSize", 0); + static LLUICachedControl close_box_from_top ("UICloseBoxFromTop", 0); + LLRect buttons_rect; + S32 button_count = 0; + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + if (!mButtons[i]) + { + continue; + } + + bool enabled = mButtonsEnabled[i]; + if (i == BUTTON_HELP) + { + // don't show the help button if the floater is minimized + // or if it is a docked tear-off floater + if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff)) + { + enabled = false; + } + } + if (i == BUTTON_CLOSE && mButtonScale != 1.f) + { + //*HACK: always render close button for hosted floaters so + //that users don't accidentally hit the button when + //closing multiple windows in the chatterbox + enabled = true; + } + + mButtons[i]->setEnabled(enabled); + + if (enabled) + { + button_count++; + + LLRect btn_rect; + if (mDragOnLeft) + { + btn_rect.setLeftTopAndSize( + LLPANEL_BORDER_WIDTH, + getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * button_count, + ll_round((F32)floater_close_box_size * mButtonScale), + ll_round((F32)floater_close_box_size * mButtonScale)); + } + else + { + btn_rect.setLeftTopAndSize( + getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * button_count, + getRect().getHeight() - close_box_from_top, + ll_round((F32)floater_close_box_size * mButtonScale), + ll_round((F32)floater_close_box_size * mButtonScale)); + } + + // first time here, init 'buttons_rect' + if(1 == button_count) + { + buttons_rect = btn_rect; + } + else + { + // if mDragOnLeft=true then buttons are on top-left side vertically aligned + // title is not displayed in this case, calculating 'buttons_rect' for future use + mDragOnLeft ? buttons_rect.mBottom -= btn_rect.mBottom : + buttons_rect.mLeft = btn_rect.mLeft; + } + mButtons[i]->setRect(btn_rect); + mButtons[i]->setVisible(true); + // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater + mButtons[i]->setTabStop(i == BUTTON_RESTORE); + } + else + { + mButtons[i]->setVisible(false); + } + } + if (mDragHandle) + { + localRectToOtherView(buttons_rect, &buttons_rect, mDragHandle); + mDragHandle->setButtonsRect(buttons_rect); + } +} + +void LLFloater::drawConeToOwner(F32 &context_cone_opacity, + F32 max_cone_opacity, + LLView *owner_view, + F32 fade_time, + F32 contex_cone_in_alpha, + F32 contex_cone_out_alpha) +{ + if (owner_view + && owner_view->isInVisibleChain() + && hasFocus() + && context_cone_opacity > 0.001f + && gFocusMgr.childHasKeyboardFocus(this)) + { + // draw cone of context pointing back to owner (e.x. texture swatch) + LLRect owner_rect; + owner_view->localRectToOtherView(owner_view->getLocalRect(), &owner_rect, this); + LLRect local_rect = getLocalRect(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + LLGLEnable(GL_CULL_FACE); + gGL.begin(LLRender::QUADS); + { + gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); + gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop); + gGL.vertex2i(owner_rect.mRight, owner_rect.mTop); + gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); + gGL.vertex2i(local_rect.mRight, local_rect.mTop); + gGL.vertex2i(local_rect.mLeft, local_rect.mTop); + + gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); + gGL.vertex2i(local_rect.mLeft, local_rect.mTop); + gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); + gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); + gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom); + gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop); + + gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); + gGL.vertex2i(local_rect.mRight, local_rect.mBottom); + gGL.vertex2i(local_rect.mRight, local_rect.mTop); + gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); + gGL.vertex2i(owner_rect.mRight, owner_rect.mTop); + gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom); + + + gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity); + gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); + gGL.vertex2i(local_rect.mRight, local_rect.mBottom); + gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity); + gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom); + gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom); + } + gGL.end(); + } + + if (gFocusMgr.childHasMouseCapture(getDragHandle())) + { + context_cone_opacity = lerp(context_cone_opacity, max_cone_opacity, LLSmoothInterpolation::getInterpolant(fade_time)); + } + else + { + context_cone_opacity = lerp(context_cone_opacity, 0.f, LLSmoothInterpolation::getInterpolant(fade_time)); + } +} + +void LLFloater::buildButtons(const Params& floater_params) +{ + static LLUICachedControl floater_close_box_size ("UIFloaterCloseBoxSize", 0); + static LLUICachedControl close_box_from_top ("UICloseBoxFromTop", 0); + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + if (mButtons[i]) + { + removeChild(mButtons[i]); + delete mButtons[i]; + mButtons[i] = NULL; + } + + LLRect btn_rect; + if (mDragOnLeft) + { + btn_rect.setLeftTopAndSize( + LLPANEL_BORDER_WIDTH, + getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * (i + 1), + ll_round(floater_close_box_size * mButtonScale), + ll_round(floater_close_box_size * mButtonScale)); + } + else + { + btn_rect.setLeftTopAndSize( + getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * (i + 1), + getRect().getHeight() - close_box_from_top, + ll_round(floater_close_box_size * mButtonScale), + ll_round(floater_close_box_size * mButtonScale)); + } + + LLButton::Params p; + p.name(sButtonNames[i]); + p.rect(btn_rect); + p.image_unselected = getButtonImage(floater_params, (EFloaterButton)i); + // Selected, no matter if hovered or not, is "pressed" + LLUIImage* pressed_image = getButtonPressedImage(floater_params, (EFloaterButton)i); + p.image_selected = pressed_image; + p.image_hover_selected = pressed_image; + // Use a glow effect when the user hovers over the button + // These icons are really small, need glow amount increased + p.hover_glow_amount( 0.33f ); + p.click_callback.function(boost::bind(sButtonCallbacks[i], this)); + p.tab_stop(false); + p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT); + p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome()); + p.scale_image(true); + p.chrome(true); + + LLButton* buttonp = LLUICtrlFactory::create(p); + addChild(buttonp); + mButtons[i] = buttonp; + } + + updateTitleButtons(); +} + +// static +LLUIImage* LLFloater::getButtonImage(const Params& p, EFloaterButton e) +{ + switch(e) + { + default: + case BUTTON_CLOSE: + return p.close_image; + case BUTTON_RESTORE: + return p.restore_image; + case BUTTON_MINIMIZE: + return p.minimize_image; + case BUTTON_TEAR_OFF: + return p.tear_off_image; + case BUTTON_DOCK: + return p.dock_image; + case BUTTON_HELP: + return p.help_image; + } +} + +// static +LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e) +{ + switch(e) + { + default: + case BUTTON_CLOSE: + return p.close_pressed_image; + case BUTTON_RESTORE: + return p.restore_pressed_image; + case BUTTON_MINIMIZE: + return p.minimize_pressed_image; + case BUTTON_TEAR_OFF: + return p.tear_off_pressed_image; + case BUTTON_DOCK: + return p.dock_pressed_image; + case BUTTON_HELP: + return p.help_pressed_image; + } +} + +// static +std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome) +{ + // EXT-4081 (Lag Meter: Ctrl+W does not close floater) + // If floater is chrome set 'Close' text for close button's tooltip + if(is_chrome && BUTTON_CLOSE == e) + { + static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME"); + return close_tooltip_chrome; + } + // TODO: per-floater localizable tooltips set in XML + return sButtonToolTips[e]; +} + +///////////////////////////////////////////////////// +// LLFloaterView + +static LLDefaultChildRegistry::Register r("floater_view"); + +LLFloaterView::LLFloaterView (const Params& p) +: LLUICtrl (p), + mFocusCycleMode(false), + mMinimizePositionVOffset(0), + mSnapOffsetBottom(0), + mSnapOffsetRight(0) +{ + mSnapView = getHandle(); +} + +// By default, adjust vertical. +void LLFloaterView::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLView::reshape(width, height, called_from_parent); + + mLastSnapRect = getSnapRect(); + + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + LLFloater* floaterp = dynamic_cast(viewp); + if (floaterp->isDependent()) + { + // dependents are moved with their "dependee" + continue; + } + + if (!floaterp->isMinimized() && floaterp->getCanDrag()) + { + LLRect old_rect = floaterp->getRect(); + floaterp->applyPositioning(NULL, false); + LLRect new_rect = floaterp->getRect(); + + //LLRect r = floaterp->getRect(); + + //// Compute absolute distance from each edge of screen + //S32 left_offset = llabs(r.mLeft - 0); + //S32 right_offset = llabs(old_right - r.mRight); + + //S32 top_offset = llabs(old_top - r.mTop); + //S32 bottom_offset = llabs(r.mBottom - 0); + + S32 translate_x = new_rect.mLeft - old_rect.mLeft; + S32 translate_y = new_rect.mBottom - old_rect.mBottom; + + //if (left_offset > right_offset) + //{ + // translate_x = new_right - old_right; + //} + + //if (top_offset < bottom_offset) + //{ + // translate_y = new_top - old_top; + //} + + // don't reposition immovable floaters + //if (floaterp->getCanDrag()) + //{ + // floaterp->translate(translate_x, translate_y); + //} + for (LLHandle dependent_floater : floaterp->mDependents) + { + if (dependent_floater.get()) + { + dependent_floater.get()->translate(translate_x, translate_y); + } + } + } + } +} + + +void LLFloaterView::restoreAll() +{ + // make sure all subwindows aren't minimized + child_list_t child_list = *(getChildList()); + for (LLView* child : child_list) + { + LLFloater* floaterp = dynamic_cast(child); + if (floaterp) + { + floaterp->setMinimized(false); + } + } + + // *FIX: make sure dependents are restored + + // children then deleted by default view constructor +} + + +LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor ) +{ + LLRect base_rect = reference_floater->getRect(); + LLRect::tCoordType width = neighbor->getRect().getWidth(); + LLRect::tCoordType height = neighbor->getRect().getHeight(); + LLRect new_rect = neighbor->getRect(); + + LLRect expanded_base_rect = base_rect; + expanded_base_rect.stretch(10); + for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin(); + dependent_it != reference_floater->mDependents.end(); ++dependent_it) + { + LLFloater* sibling = dependent_it->get(); + // check for dependents within 10 pixels of base floater + if (sibling && + sibling != neighbor && + sibling->getVisible() && + expanded_base_rect.overlaps(sibling->getRect())) + { + base_rect.unionWith(sibling->getRect()); + } + } + + LLRect::tCoordType left_margin = llmax(0, base_rect.mLeft); + LLRect::tCoordType right_margin = llmax(0, getRect().getWidth() - base_rect.mRight); + LLRect::tCoordType top_margin = llmax(0, getRect().getHeight() - base_rect.mTop); + LLRect::tCoordType bottom_margin = llmax(0, base_rect.mBottom); + + // find position for floater in following order + // right->left->bottom->top + for (S32 i = 0; i < 5; i++) + { + if (right_margin > width) + { + new_rect.translate(base_rect.mRight - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mTop); + return new_rect; + } + else if (left_margin > width) + { + new_rect.translate(base_rect.mLeft - neighbor->getRect().mRight, base_rect.mTop - neighbor->getRect().mTop); + return new_rect; + } + else if (bottom_margin > height) + { + new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mBottom - neighbor->getRect().mTop); + return new_rect; + } + else if (top_margin > height) + { + new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mBottom); + return new_rect; + } + + // keep growing margins to find "best" fit + left_margin += 20; + right_margin += 20; + top_margin += 20; + bottom_margin += 20; + } + + // didn't find anything, return initial rect + return new_rect; +} + + +void LLFloaterView::bringToFront(LLFloater* child, bool give_focus, bool restore) +{ + if (!child) + return; + + LLFloater* front_child = mFrontChildHandle.get(); + if (front_child == child) + { + if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child)) + { + child->setFocus(true); + } + return; + } + + if (front_child && front_child->getVisible()) + { + front_child->goneFromFront(); + } + + mFrontChildHandle = child->getHandle(); + + // *TODO: make this respect floater's mAutoFocus value, instead of + // using parameter + if (child->getHost()) + { + // this floater is hosted elsewhere and hence not one of our children, abort + return; + } + std::vector floaters_to_move; + // Look at all floaters...tab + for (child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it) + { + LLFloater* floater = dynamic_cast(*child_it); + + // ...but if I'm a dependent floater... + if (floater && child->isDependent()) + { + // ...look for floaters that have me as a dependent... + LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle()); + + if (found_dependent != floater->mDependents.end()) + { + // ...and make sure all children of that floater (including me) are brought to front... + for (LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); + dependent_it != floater->mDependents.end(); ++dependent_it) + { + LLFloater* sibling = dependent_it->get(); + if (sibling) + { + floaters_to_move.push_back(sibling); + } + } + //...before bringing my parent to the front... + floaters_to_move.push_back(floater); + } + } + } + + std::vector::iterator floater_it; + for(floater_it = floaters_to_move.begin(); floater_it != floaters_to_move.end(); ++floater_it) + { + LLFloater* floaterp = *floater_it; + sendChildToFront(floaterp); + + // always unminimize dependee, but allow dependents to stay minimized + if (!floaterp->isDependent()) + { + floaterp->setMinimized(false); + } + } + floaters_to_move.clear(); + + // ...then bringing my own dependents to the front... + for (LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin(); + dependent_it != child->mDependents.end(); ++dependent_it) + { + LLFloater* dependent = dependent_it->get(); + if (dependent) + { + sendChildToFront(dependent); + } + } + + // ...and finally bringing myself to front + // (do this last, so that I'm left in front at end of this call) + if (*beginChild() != child) + { + sendChildToFront(child); + } + + if(restore) + { + child->setMinimized(false); + } + + if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) + { + child->setFocus(true); + // floater did not take focus, so relinquish focus to world + if (!child->hasFocus()) + { + gFocusMgr.setKeyboardFocus(NULL); + } + } +} + +void LLFloaterView::highlightFocusedFloater() +{ + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLFloater *floater = (LLFloater *)(*child_it); + + // skip dependent floaters, as we'll handle them in a batch along with their dependee(?) + if (floater->isDependent()) + { + continue; + } + + bool floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater); + for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); + dependent_it != floater->mDependents.end(); + ++dependent_it) + { + LLFloater* dependent_floaterp = dependent_it->get(); + if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp)) + { + floater_or_dependent_has_focus = true; + } + } + + // now set this floater and all its dependents + floater->setForeground(floater_or_dependent_has_focus); + + for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); + dependent_it != floater->mDependents.end(); ) + { + LLFloater* dependent_floaterp = dependent_it->get(); + if (dependent_floaterp) + { + dependent_floaterp->setForeground(floater_or_dependent_has_focus); + } + ++dependent_it; + } + + floater->cleanupHandles(); + } +} + +LLFloater* LLFloaterView::getFrontmostClosableFloater() +{ + child_list_const_iter_t child_it; + LLFloater* frontmost_floater = NULL; + + for ( child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + frontmost_floater = (LLFloater *)(*child_it); + + if (frontmost_floater->isInVisibleChain() && frontmost_floater->isCloseable()) + { + return frontmost_floater; + } + } + + return NULL; +} + +void LLFloaterView::unhighlightFocusedFloater() +{ + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLFloater *floater = (LLFloater *)(*child_it); + + floater->setForeground(false); + } +} + +void LLFloaterView::focusFrontFloater() +{ + LLFloater* floaterp = getFrontmost(); + if (floaterp) + { + floaterp->setFocus(true); + } +} + +void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom) +{ + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + static LLUICachedControl minimized_width ("UIMinimizedWidth", 0); + LLRect snap_rect_local = getLocalSnapRect(); + snap_rect_local.mTop += mMinimizePositionVOffset; + for(S32 col = snap_rect_local.mLeft; + col < snap_rect_local.getWidth() - minimized_width; + col += minimized_width) + { + for(S32 row = snap_rect_local.mTop - floater_header_size; + row > floater_header_size; + row -= floater_header_size ) //loop rows + { + + bool foundGap = true; + for(child_list_const_iter_t child_it = getChildList()->begin(); + child_it != getChildList()->end(); + ++child_it) //loop floaters + { + // Examine minimized children. + LLFloater* floater = dynamic_cast(*child_it); + if(floater->isMinimized()) + { + LLRect r = floater->getRect(); + if((r.mBottom < (row + floater_header_size)) + && (r.mBottom > (row - floater_header_size)) + && (r.mLeft < (col + minimized_width)) + && (r.mLeft > (col - minimized_width))) + { + // needs the check for off grid. can't drag, + // but window resize makes them off + foundGap = false; + break; + } + } + } //done floaters + if(foundGap) + { + *left = col; + *bottom = row; + return; //done + } + } //done this col + } + + // crude - stack'em all at 0,0 when screen is full of minimized + // floaters. + *left = snap_rect_local.mLeft; + *bottom = snap_rect_local.mBottom; +} + + +void LLFloaterView::destroyAllChildren() +{ + LLView::deleteAllChildren(); +} + +void LLFloaterView::closeAllChildren(bool app_quitting) +{ + // iterate over a copy of the list, because closing windows will destroy + // some windows on the list. + child_list_t child_list = *(getChildList()); + + for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it) + { + LLView* viewp = *it; + child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp); + if (exists == getChildList()->end()) + { + // this floater has already been removed + continue; + } + + LLFloater* floaterp = dynamic_cast(viewp); + + // Attempt to close floater. This will cause the "do you want to save" + // dialogs to appear. + // Skip invisible floaters if we're not quitting (STORM-192). + if (floaterp->canClose() && !floaterp->isDead() && + (app_quitting || floaterp->getVisible())) + { + floaterp->closeFloater(app_quitting); + } + } +} + +void LLFloaterView::hiddenFloaterClosed(LLFloater* floater) +{ + for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end(); + it != end_it; + ++it) + { + if (it->first.get() == floater) + { + it->second.disconnect(); + mHiddenFloaters.erase(it); + break; + } + } +} + +void LLFloaterView::hideAllFloaters() +{ + child_list_t child_list = *(getChildList()); + + for (child_list_iter_t it = child_list.begin(); it != child_list.end(); ++it) + { + LLFloater* floaterp = dynamic_cast(*it); + if (floaterp && floaterp->getVisible()) + { + floaterp->setVisible(false); + boost::signals2::connection connection = floaterp->mCloseSignal.connect(boost::bind(&LLFloaterView::hiddenFloaterClosed, this, floaterp)); + mHiddenFloaters.push_back(std::make_pair(floaterp->getHandle(), connection)); + } + } +} + +void LLFloaterView::showHiddenFloaters() +{ + for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end(); + it != end_it; + ++it) + { + LLFloater* floaterp = it->first.get(); + if (floaterp) + { + floaterp->setVisible(true); + } + it->second.disconnect(); + } + mHiddenFloaters.clear(); +} + +bool LLFloaterView::allChildrenClosed() +{ + // see if there are any visible floaters (some floaters "close" + // by setting themselves invisible) + for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it) + { + LLFloater* floaterp = dynamic_cast(*it); + + if (floaterp->getVisible() && !floaterp->isDead() && floaterp->isCloseable()) + { + return false; + } + } + return true; +} + +void LLFloaterView::shiftFloaters(S32 x_offset, S32 y_offset) +{ + for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it) + { + LLFloater* floaterp = dynamic_cast(*it); + + if (floaterp && floaterp->isMinimized()) + { + floaterp->translate(x_offset, y_offset); + } + } +} + +void LLFloaterView::refresh() +{ + LLRect snap_rect = getSnapRect(); + if (snap_rect != mLastSnapRect) + { + reshape(getRect().getWidth(), getRect().getHeight(), true); + } + + // Constrain children to be entirely on the screen + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLFloater* floaterp = dynamic_cast(*child_it); + if (floaterp && floaterp->getVisible() ) + { + // minimized floaters are kept fully onscreen + adjustToFitScreen(floaterp, !floaterp->isMinimized()); + } + } +} + +void LLFloaterView::adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars/* = false*/) +{ + if (floater->getParent() != this) + { + // floater is hosted elsewhere, so ignore + return; + } + + if (floater->getDependee() && + floater->getDependee() == floater->getSnapTarget().get()) + { + // floater depends on other and snaps to it, so ignore + return; + } + + LLRect::tCoordType screen_width = getSnapRect().getWidth(); + LLRect::tCoordType screen_height = getSnapRect().getHeight(); + + // only automatically resize non-minimized, resizable floaters + if( floater->isResizable() && !floater->isMinimized() ) + { + LLRect view_rect = floater->getRect(); + S32 old_width = view_rect.getWidth(); + S32 old_height = view_rect.getHeight(); + S32 min_width; + S32 min_height; + floater->getResizeLimits( &min_width, &min_height ); + + // Make sure floater isn't already smaller than its min height/width? + S32 new_width = llmax( min_width, old_width ); + S32 new_height = llmax( min_height, old_height); + + if((new_width > screen_width) || (new_height > screen_height)) + { + // We have to make this window able to fit on screen + new_width = llmin(new_width, screen_width); + new_height = llmin(new_height, screen_height); + + // ...while respecting minimum width/height + new_width = llmax(new_width, min_width); + new_height = llmax(new_height, min_height); + + LLRect new_rect; + new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height); + + floater->setShape(new_rect); + + if (floater->followsRight()) + { + floater->translate(old_width - new_width, 0); + } + + if (floater->followsTop()) + { + floater->translate(0, old_height - new_height); + } + } + } + + const LLRect& constraint = snap_in_toolbars ? getSnapRect() : gFloaterView->getRect(); + S32 min_overlap_pixels = allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX; + + floater->fitWithDependentsOnScreen(mToolbarLeftRect, mToolbarBottomRect, mToolbarRightRect, constraint, min_overlap_pixels); +} + +void LLFloaterView::draw() +{ + refresh(); + + // hide focused floater if in cycle mode, so that it can be drawn on top + LLFloater* focused_floater = getFocusedFloater(); + + if (mFocusCycleMode && focused_floater) + { + child_list_const_iter_t child_it = getChildList()->begin(); + for (;child_it != getChildList()->end(); ++child_it) + { + if ((*child_it) != focused_floater) + { + drawChild(*child_it); + } + } + + drawChild(focused_floater, -TABBED_FLOATER_OFFSET, TABBED_FLOATER_OFFSET); + } + else + { + LLView::draw(); + } +} + +LLRect LLFloaterView::getSnapRect() const +{ + LLRect snap_rect = getLocalRect(); + + LLView* snap_view = mSnapView.get(); + if (snap_view) + { + snap_view->localRectToOtherView(snap_view->getLocalRect(), &snap_rect, this); + } + + return snap_rect; +} + +LLFloater *LLFloaterView::getFocusedFloater() const +{ + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + if ((*child_it)->isCtrl()) + { + LLFloater* ctrlp = dynamic_cast(*child_it); + if ( ctrlp && ctrlp->hasFocus() ) + { + return ctrlp; + } + } + } + return NULL; +} + +LLFloater *LLFloaterView::getFrontmost() const +{ + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + if ( viewp->getVisible() && !viewp->isDead()) + { + return (LLFloater *)viewp; + } + } + return NULL; +} + +LLFloater *LLFloaterView::getBackmost() const +{ + LLFloater* back_most = NULL; + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + if ( viewp->getVisible() ) + { + back_most = (LLFloater *)viewp; + } + } + return back_most; +} + +void LLFloaterView::syncFloaterTabOrder() +{ + LLFloater* front_child = mFrontChildHandle.get(); + if (front_child && front_child->getIsChrome()) + return; + + // look for a visible modal dialog, starting from first + LLModalDialog* modal_dialog = NULL; + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLModalDialog* dialog = dynamic_cast(*child_it); + if (dialog && dialog->isModal() && dialog->getVisible()) + { + modal_dialog = dialog; + break; + } + } + + if (modal_dialog) + { + // If we have a visible modal dialog, make sure that it has focus + LLUI::getInstance()->addPopup(modal_dialog); + + if( !gFocusMgr.childHasKeyboardFocus( modal_dialog ) ) + { + modal_dialog->setFocus(true); + } + + if( !gFocusMgr.childHasMouseCapture( modal_dialog ) ) + { + gFocusMgr.setMouseCapture( modal_dialog ); + } + } + else + { + // otherwise, make sure the focused floater is in the front of the child list + for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it) + { + LLFloater* floaterp = dynamic_cast(*child_it); + if (gFocusMgr.childHasKeyboardFocus(floaterp)) + { + LLFloater* front_child = mFrontChildHandle.get(); + if (front_child != floaterp) + { + // Grab a list of the top floaters that want to stay on top of the focused floater + std::list listTop; + if (front_child && !front_child->canFocusStealFrontmost()) + { + for (LLView* childp : *getChildList()) + { + LLFloater* child_floaterp = static_cast(childp); + if (child_floaterp->canFocusStealFrontmost()) + break; + listTop.push_back(child_floaterp); + } + } + + bringToFront(floaterp, false); + + // Restore top floaters + if (!listTop.empty()) + { + for (LLView* childp : listTop) + { + sendChildToFront(childp); + } + mFrontChildHandle = listTop.back()->getHandle(); + } + } + + break; + } + } + } +} + +LLFloater* LLFloaterView::getParentFloater(LLView* viewp) const +{ + LLView* parentp = viewp->getParent(); + + while(parentp && parentp != this) + { + viewp = parentp; + parentp = parentp->getParent(); + } + + if (parentp == this) + { + return dynamic_cast(viewp); + } + + return NULL; +} + +S32 LLFloaterView::getZOrder(LLFloater* child) +{ + S32 rv = 0; + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + if(viewp == child) + { + break; + } + ++rv; + } + return rv; +} + +void LLFloaterView::pushVisibleAll(bool visible, const skip_list_t& skip_list) +{ + for (child_list_const_iter_t child_iter = getChildList()->begin(); + child_iter != getChildList()->end(); ++child_iter) + { + LLView *view = *child_iter; + if (skip_list.find(view) == skip_list.end()) + { + view->pushVisible(visible); + } + } + + LLFloaterReg::blockShowFloaters(true); +} + +void LLFloaterView::popVisibleAll(const skip_list_t& skip_list) +{ + // 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()) + { + view->popVisible(); + } + } + + LLFloaterReg::blockShowFloaters(false); +} + +void LLFloaterView::setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect) +{ + switch (tb) + { + case LLToolBarEnums::TOOLBAR_LEFT: + mToolbarLeftRect = toolbar_rect; + break; + case LLToolBarEnums::TOOLBAR_BOTTOM: + mToolbarBottomRect = toolbar_rect; + break; + case LLToolBarEnums::TOOLBAR_RIGHT: + mToolbarRightRect = toolbar_rect; + break; + default: + LL_WARNS() << "setToolbarRect() passed odd toolbar number " << (S32) tb << LL_ENDL; + break; + } +} + +void LLFloater::setInstanceName(const std::string& name) +{ + if (name != mInstanceName) + { + llassert_always(mInstanceName.empty()); + mInstanceName = name; + if (!mInstanceName.empty()) + { + std::string ctrl_name = getControlName(mInstanceName, mKey); + initRectControl(); + if (!mVisibilityControl.empty()) + { + mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name); + } + if(!mDocStateControl.empty()) + { + mDocStateControl = LLFloaterReg::declareDockStateControl(ctrl_name); + } + } +} +} + +void LLFloater::setKey(const LLSD& newkey) +{ + // Note: We don't have to do anything special with registration when we change keys + mKey = newkey; +} + +//static +void LLFloater::setupParamsForExport(Params& p, LLView* parent) +{ + // Do rectangle munging to topleft layout first + LLPanel::setupParamsForExport(p, parent); + + // Copy the rectangle out to apply layout constraints + LLRect rect = p.rect; + + // Null out other settings + p.rect.left.setProvided(false); + p.rect.top.setProvided(false); + p.rect.right.setProvided(false); + p.rect.bottom.setProvided(false); + + // Explicitly set width/height + p.rect.width.set( rect.getWidth(), true ); + p.rect.height.set( rect.getHeight(), true ); + + // If you can't resize this floater, don't export min_height + // and min_width + bool can_resize = p.can_resize; + if (!can_resize) + { + p.min_height.setProvided(false); + p.min_width.setProvided(false); + } +} + +void LLFloater::initFromParams(const LLFloater::Params& p) +{ + // *NOTE: We have too many classes derived from LLFloater to retrofit them + // all to pass in params via constructors. So we use this method. + + // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible + LLPanel::initFromParams(p); + + // override any follows flags + if (mPositioning != LLFloaterEnums::POSITIONING_SPECIFIED) + { + setFollows(FOLLOWS_NONE); + } + + mTitle = p.title; + mShortTitle = p.short_title; + applyTitle(); + + setCanTearOff(p.can_tear_off); + setCanMinimize(p.can_minimize); + setCanClose(p.can_close); + setCanDock(p.can_dock); + setCanResize(p.can_resize); + setResizeLimits(p.min_width, p.min_height); + + mDragOnLeft = p.can_drag_on_left; + mHeaderHeight = p.header_height; + mLegacyHeaderHeight = p.legacy_header_height; + mSingleInstance = p.single_instance; + mReuseInstance = p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance; + + mDefaultRelativeX = p.rel_x; + mDefaultRelativeY = p.rel_y; + + mPositioning = p.positioning; + mAutoClose = p.auto_close; + + mSaveRect = p.save_rect; + if (p.save_visibility) + { + mVisibilityControl = "t"; // flag to build mVisibilityControl name once mInstanceName is set + } + if(p.save_dock_state) + { + mDocStateControl = "t"; // flag to build mDocStateControl name once mInstanceName is set + } + + // open callback + if (p.open_callback.isProvided()) + { + setOpenCallback(initCommitCallback(p.open_callback)); + } + // close callback + if (p.close_callback.isProvided()) + { + setCloseCallback(initCommitCallback(p.close_callback)); + } + + if (mDragHandle) + { + mDragHandle->setTitleVisible(p.show_title); + } +} + +boost::signals2::connection LLFloater::setMinimizeCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMinimizeSignal) mMinimizeSignal = new commit_signal_t(); + return mMinimizeSignal->connect(cb); +} + +boost::signals2::connection LLFloater::setOpenCallback( const commit_signal_t::slot_type& cb ) +{ + return mOpenSignal.connect(cb); +} + +boost::signals2::connection LLFloater::setCloseCallback( const commit_signal_t::slot_type& cb ) +{ + return mCloseSignal.connect(cb); +} + +bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node) +{ + LL_PROFILE_ZONE_SCOPED; + Params default_params(LLUICtrlFactory::getDefaultParams()); + Params params(default_params); + + LLXUIParser parser; + parser.readXUI(node, params, filename); // *TODO: Error checking + + std::string xml_filename = params.filename; + + if (!xml_filename.empty()) + { + LLXMLNodePtr referenced_xml; + + if (output_node) + { + //if we are exporting, we want to export the current xml + //not the referenced xml + Params output_params; + parser.readXUI(node, output_params, LLUICtrlFactory::getInstance()->getCurFileName()); + setupParamsForExport(output_params, parent); + output_node->setName(node->getName()->mString); + parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); + return true; + } + + LLUICtrlFactory::instance().pushFileName(xml_filename); + + if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml)) + { + LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL; + + return false; + } + + Params referenced_params; + parser.readXUI(referenced_xml, referenced_params, LLUICtrlFactory::getInstance()->getCurFileName()); + params.fillFrom(referenced_params); + + // add children using dimensions from referenced xml for consistent layout + setShape(params.rect); + LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance()); + + LLUICtrlFactory::instance().popFileName(); + } + + + if (output_node) + { + Params output_params(params); + setupParamsForExport(output_params, parent); + output_node->setName(node->getName()->mString); + parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); + } + + // Default floater position to top-left corner of screen + // However, some legacy floaters have explicit top or bottom + // coordinates set, so respect their wishes. + if (!params.rect.top.isProvided() && !params.rect.bottom.isProvided()) + { + params.rect.top.set(0); + } + if (!params.rect.left.isProvided() && !params.rect.right.isProvided()) + { + params.rect.left.set(0); + } + params.from_xui = true; + applyXUILayout(params, parent, parent == gFloaterView ? gFloaterView->getSnapRect() : parent->getLocalRect()); + initFromParams(params); + + initFloater(params); + + LLMultiFloater* last_host = LLFloater::getFloaterHost(); + if (node->hasName("multi_floater")) + { + LLFloater::setFloaterHost((LLMultiFloater*) this); + } + + LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node); + + if (node->hasName("multi_floater")) + { + LLFloater::setFloaterHost(last_host); + } + + // HACK: When we changed the header height to 25 pixels in Viewer 2, rather + // than re-layout all the floaters we use this value in pixels to make the + // whole floater bigger and change the top-left coordinate for widgets. + // The goal is to eventually set mLegacyHeaderHeight to zero, which would + // make the top-left corner for widget layout the same as the top-left + // corner of the window's content area. James + S32 header_stretch = (mHeaderHeight - mLegacyHeaderHeight); + if (header_stretch > 0) + { + // Stretch the floater vertically, don't move widgets + LLRect rect = getRect(); + rect.mTop += header_stretch; + + // This will also update drag handle, title bar, close box, etc. + setRect(rect); + } + + bool result; + result = postBuild(); + + if (!result) + { + LL_ERRS() << "Failed to construct floater " << getName() << LL_ENDL; + } + + applyRectControl(); // If we have a saved rect control, apply it + gFloaterView->adjustToFitScreen(this, false); // Floaters loaded from XML should all fit on screen + + moveResizeHandlesToFront(); + + applyDockState(); + + return true; // *TODO: Error checking +} + +bool LLFloater::isShown() const +{ + return ! isMinimized() && isInVisibleChain(); +} + +bool LLFloater::isDetachedAndNotMinimized() +{ + return !getHost() && !isMinimized(); +} + +/* static */ +bool LLFloater::isShown(const LLFloater* floater) +{ + return floater && floater->isShown(); +} + +/* static */ +bool LLFloater::isMinimized(const LLFloater* floater) +{ + return floater && floater->isMinimized(); +} + +/* static */ +bool LLFloater::isVisible(const LLFloater* floater) +{ + return floater && floater->getVisible(); +} + +bool LLFloater::buildFromFile(const std::string& filename) +{ + LL_PROFILE_ZONE_SCOPED; + LLXMLNodePtr root; + + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + { + LL_WARNS() << "Couldn't find (or parse) floater from: " << filename << LL_ENDL; + return false; + } + + // root must be called floater + if( !(root->hasName("floater") || root->hasName("multi_floater")) ) + { + LL_WARNS() << "Root node should be named floater in: " << filename << LL_ENDL; + return false; + } + + bool res = true; + + LL_DEBUGS() << "Building floater " << filename << LL_ENDL; + LLUICtrlFactory::instance().pushFileName(filename); + { + if (!getFactoryMap().empty()) + { + LLPanel::sFactoryStack.push_front(&getFactoryMap()); + } + + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + getCommitCallbackRegistrar().pushScope(); + getEnableCallbackRegistrar().pushScope(); + + res = initFloaterXML(root, getParent(), filename, NULL); + + setXMLFilename(filename); + + getCommitCallbackRegistrar().popScope(); + getEnableCallbackRegistrar().popScope(); + + if (!getFactoryMap().empty()) + { + LLPanel::sFactoryStack.pop_front(); + } + } + LLUICtrlFactory::instance().popFileName(); + + return res; +} + +void LLFloater::stackWith(LLFloater& other) +{ + static LLUICachedControl floater_offset ("UIFloaterOffset", 16); + + LLRect next_rect; + if (other.getHost()) + { + next_rect = other.getHost()->getRect(); + } + else + { + next_rect = other.getRect(); + } + next_rect.translate(floater_offset, -floater_offset); + + const LLRect& rect = getControlGroup()->getRect(mRectControl); + if (rect.notEmpty() && !mDefaultRectForGroup && mResizable) + { + next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight())); + } + else + { + next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight()); + } + setShape(next_rect); + + if (!other.getHost()) + { + other.mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP; + other.setFollows(FOLLOWS_LEFT | FOLLOWS_TOP); + } +} + +void LLFloater::applyRelativePosition() +{ + LLRect snap_rect = gFloaterView->getSnapRect(); + LLRect floater_view_screen_rect = gFloaterView->calcScreenRect(); + snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom); + LLRect floater_screen_rect = calcScreenRect(); + + LLCoordGL new_center = mPosition.convert(); + LLCoordGL cur_center(floater_screen_rect.getCenterX(), floater_screen_rect.getCenterY()); + translate(new_center.mX - cur_center.mX, new_center.mY - cur_center.mY); +} + + +LLCoordFloater::LLCoordFloater(F32 x, F32 y, LLFloater& floater) +: coord_t((S32)x, (S32)y) +{ + mFloater = floater.getHandle(); +} + + +LLCoordFloater::LLCoordFloater(const LLCoordCommon& other, LLFloater& floater) +{ + mFloater = floater.getHandle(); + convertFromCommon(other); +} + +LLCoordFloater& LLCoordFloater::operator=(const LLCoordFloater& other) +{ + mFloater = other.mFloater; + coord_t::operator =(other); + return *this; +} + +void LLCoordFloater::setFloater(LLFloater& floater) +{ + mFloater = floater.getHandle(); +} + +bool LLCoordFloater::operator==(const LLCoordFloater& other) const +{ + return mX == other.mX && mY == other.mY && mFloater == other.mFloater; +} + +LLCoordCommon LL_COORD_FLOATER::convertToCommon() const +{ + const LLCoordFloater& self = static_cast(LLCoordFloater::getTypedCoords(*this)); + + LLRect snap_rect = gFloaterView->getSnapRect(); + LLRect floater_view_screen_rect = gFloaterView->calcScreenRect(); + snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom); + + LLFloater* floaterp = mFloater.get(); + S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0; + S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0; + LLCoordCommon out; + if (self.mX < -0.5f) + { + out.mX = ll_round(rescale(self.mX, -1.f, -0.5f, snap_rect.mLeft - (floater_width - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mLeft)); + } + else if (self.mX > 0.5f) + { + out.mX = ll_round(rescale(self.mX, 0.5f, 1.f, snap_rect.mRight - floater_width, snap_rect.mRight - FLOATER_MIN_VISIBLE_PIXELS)); + } + else + { + out.mX = ll_round(rescale(self.mX, -0.5f, 0.5f, snap_rect.mLeft, snap_rect.mRight - floater_width)); + } + + if (self.mY < -0.5f) + { + out.mY = ll_round(rescale(self.mY, -1.f, -0.5f, snap_rect.mBottom - (floater_height - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mBottom)); + } + else if (self.mY > 0.5f) + { + out.mY = ll_round(rescale(self.mY, 0.5f, 1.f, snap_rect.mTop - floater_height, snap_rect.mTop - FLOATER_MIN_VISIBLE_PIXELS)); + } + else + { + out.mY = ll_round(rescale(self.mY, -0.5f, 0.5f, snap_rect.mBottom, snap_rect.mTop - floater_height)); + } + + // return center point instead of lower left + out.mX += floater_width / 2; + out.mY += floater_height / 2; + + return out; +} + +void LL_COORD_FLOATER::convertFromCommon(const LLCoordCommon& from) +{ + LLCoordFloater& self = static_cast(LLCoordFloater::getTypedCoords(*this)); + LLRect snap_rect = gFloaterView->getSnapRect(); + LLRect floater_view_screen_rect = gFloaterView->calcScreenRect(); + snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom); + + + LLFloater* floaterp = mFloater.get(); + S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0; + S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0; + + S32 from_x = from.mX - floater_width / 2; + S32 from_y = from.mY - floater_height / 2; + + if (from_x < snap_rect.mLeft) + { + self.mX = rescale(from_x, snap_rect.mLeft - (floater_width - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mLeft, -1.f, -0.5f); + } + else if (from_x + floater_width > snap_rect.mRight) + { + self.mX = rescale(from_x, snap_rect.mRight - floater_width, snap_rect.mRight - FLOATER_MIN_VISIBLE_PIXELS, 0.5f, 1.f); + } + else + { + self.mX = rescale(from_x, snap_rect.mLeft, snap_rect.mRight - floater_width, -0.5f, 0.5f); + } + + if (from_y < snap_rect.mBottom) + { + self.mY = rescale(from_y, snap_rect.mBottom - (floater_height - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mBottom, -1.f, -0.5f); + } + else if (from_y + floater_height > snap_rect.mTop) + { + self.mY = rescale(from_y, snap_rect.mTop - floater_height, snap_rect.mTop - FLOATER_MIN_VISIBLE_PIXELS, 0.5f, 1.f); + } + else + { + self.mY = rescale(from_y, snap_rect.mBottom, snap_rect.mTop - floater_height, -0.5f, 0.5f); + } +} diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index ed3aed4db6..3d75c42f60 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -1,641 +1,641 @@ -/** - * @file llfloater.h - * @brief LLFloater base class - * - * $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$ - */ - -// Floating "windows" within the GL display, like the inventory floater, -// mini-map floater, etc. - - -#ifndef LL_FLOATER_H -#define LL_FLOATER_H - -#include "llpanel.h" -#include "lltoolbar.h" -#include "lluuid.h" -//#include "llnotificationsutil.h" -#include -#include - -class LLDragHandle; -class LLResizeHandle; -class LLResizeBar; -class LLButton; -class LLMultiFloater; -class LLFloater; - - -const bool RESIZE_YES = true; -const bool RESIZE_NO = false; - -const bool DRAG_ON_TOP = false; -const bool DRAG_ON_LEFT = true; - -const bool MINIMIZE_YES = true; -const bool MINIMIZE_NO = false; - -const bool CLOSE_YES = true; -const bool CLOSE_NO = false; - -const bool ADJUST_VERTICAL_YES = true; -const bool ADJUST_VERTICAL_NO = false; - -const F32 CONTEXT_CONE_IN_ALPHA = 0.f; -const F32 CONTEXT_CONE_OUT_ALPHA = 1.f; -const F32 CONTEXT_CONE_FADE_TIME = .08f; - -namespace LLFloaterEnums -{ - enum EOpenPositioning - { - POSITIONING_RELATIVE, - POSITIONING_CASCADING, - POSITIONING_CASCADE_GROUP, - POSITIONING_CENTERED, - POSITIONING_SPECIFIED, - POSITIONING_COUNT - }; -} - -namespace LLInitParam -{ - template<> - struct TypeValues : public TypeValuesHelper - { - static void declareValues(); - }; -} - -struct LL_COORD_FLOATER -{ - typedef F32 value_t; - - LLCoordCommon convertToCommon() const; - void convertFromCommon(const LLCoordCommon& from); -protected: - LLHandle mFloater; -}; - -struct LLCoordFloater : LLCoord -{ - typedef LLCoord coord_t; - - LLCoordFloater() {} - LLCoordFloater(F32 x, F32 y, LLFloater& floater); - LLCoordFloater(const LLCoordCommon& other, LLFloater& floater); - - LLCoordFloater& operator=(const LLCoordCommon& other) - { - convertFromCommon(other); - return *this; - } - - LLCoordFloater& operator=(const LLCoordFloater& other); - - bool operator==(const LLCoordFloater& other) const; - bool operator!=(const LLCoordFloater& other) const { return !(*this == other); } - - void setFloater(LLFloater& floater); -}; - -class LLFloater : public LLPanel, public LLInstanceTracker -{ - friend class LLFloaterView; - friend class LLFloaterReg; - friend class LLMultiFloater; - -public: - - struct KeyCompare - { -// static bool compare(const LLSD& a, const LLSD& b); - static bool equate(const LLSD& a, const LLSD& b); -/*==========================================================================*| - bool operator()(const LLSD& a, const LLSD& b) const - { - return compare(a, b); - } -|*==========================================================================*/ - }; - - enum EFloaterButton - { - BUTTON_CLOSE = 0, - BUTTON_RESTORE, - BUTTON_MINIMIZE, - BUTTON_TEAR_OFF, - BUTTON_DOCK, - BUTTON_HELP, - BUTTON_COUNT - }; - - struct Params - : public LLInitParam::Block - { - Optional title, - short_title; - - Optional single_instance, - reuse_instance, - can_resize, - can_minimize, - can_close, - can_drag_on_left, - can_tear_off, - save_rect, - save_visibility, - save_dock_state, - can_dock, - show_title, - auto_close; - - Optional positioning; - - Optional header_height, - legacy_header_height; // HACK see initFromXML() - - Optional rel_x, - rel_y; - - // Images for top-right controls - Optional close_image, - restore_image, - minimize_image, - tear_off_image, - dock_image, - help_image; - Optional close_pressed_image, - restore_pressed_image, - minimize_pressed_image, - tear_off_pressed_image, - dock_pressed_image, - help_pressed_image; - - Optional open_callback, - close_callback; - - Ignored follows; - - Params(); - }; - - // use this to avoid creating your own default LLFloater::Param instance - static const Params& getDefaultParams(); - - // Load translations for tooltips for standard buttons - static void initClass(); - - LLFloater(const LLSD& key, const Params& params = getDefaultParams()); - - virtual ~LLFloater(); - - // Don't export top/left for rect, only height/width - static void setupParamsForExport(Params& p, LLView* parent); - bool buildFromFile(const std::string &filename); - - boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setCloseCallback( const commit_signal_t::slot_type& cb ); - - void initFromParams(const LLFloater::Params& p); - bool initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node = NULL); - - /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); - /*virtual*/ bool canSnapTo(const LLView* other_view); - /*virtual*/ void setSnappedTo(const LLView* snap_view); - /*virtual*/ void setFocus( bool b ); - /*virtual*/ void setIsChrome(bool is_chrome); - /*virtual*/ void setRect(const LLRect &rect); - void setIsSingleInstance(bool is_single_instance); - bool getIsSingleInstance() { return mSingleInstance; } - - void initFloater(const Params& p); - - void openFloater(const LLSD& key = LLSD()); - - // If allowed, close the floater cleanly, releasing focus. - virtual void closeFloater(bool app_quitting = false); - - // Close the floater or its host. Use when hidding or toggling a floater instance. - virtual void closeHostedFloater(); - - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - /*virtual*/ void translate(S32 x, S32 y); - - // Release keyboard and mouse focus - void releaseFocus(); - - // moves to center of gFloaterView - void center(); - - LLMultiFloater* getHost(); - bool isDetachedAndNotMinimized(); - - void applyTitle(); - std::string getCurrentTitle() const; - void setTitle( const std::string& title); - std::string getTitle() const; - void setShortTitle( const std::string& short_title ); - std::string getShortTitle() const; - virtual void setMinimized(bool b); - void moveResizeHandlesToFront(); - void addDependentFloater(LLFloater* dependent, bool reposition = true, bool resize = false); - void addDependentFloater(LLHandle dependent_handle, bool reposition = true, bool resize = false); - LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); } - void removeDependentFloater(LLFloater* dependent); - void fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels); - bool isMinimized() const { return mMinimized; } - /// isShown() differs from getVisible() in that isShown() also considers - /// isMinimized(). isShown() is true only if visible and not minimized. - bool isShown() const; - /// The static isShown() can accept a NULL pointer (which of course - /// returns false). When non-NULL, it calls the non-static isShown(). - static bool isShown(const LLFloater* floater); - static bool isVisible(const LLFloater* floater); - static bool isMinimized(const LLFloater* floater); - bool isFirstLook() { return mFirstLook; } // EXT-2653: This function is necessary to prevent overlapping for secondary showed toasts - virtual bool isFrontmost(); - bool isDependent() { return !mDependeeHandle.isDead(); } - void setCanMinimize(bool can_minimize); - void setCanClose(bool can_close); - void setCanTearOff(bool can_tear_off); - virtual void setCanResize(bool can_resize); - void setCanDrag(bool can_drag); - bool getCanDrag(); - void setHost(LLMultiFloater* host); - bool isResizable() const { return mResizable; } - void setResizeLimits( S32 min_width, S32 min_height ); - void getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; } - - static std::string getControlName(const std::string& name, const LLSD& key); - static LLControlGroup* getControlGroup(); - - bool isMinimizeable() const{ return mCanMinimize; } - bool isCloseable() const{ return mCanClose; } - bool isDragOnLeft() const{ return mDragOnLeft; } - S32 getMinWidth() const{ return mMinWidth; } - S32 getMinHeight() const{ return mMinHeight; } - S32 getHeaderHeight() const { return mHeaderHeight; } - - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); - virtual bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); - - virtual bool handleScrollWheel(S32 x, S32 y, S32 mask); - - virtual void draw(); - virtual void drawShadow(LLPanel* panel); - - virtual void onOpen(const LLSD& key) {} - virtual void onClose(bool app_quitting) {} - - // This cannot be "const" until all derived floater canClose() - // methods are const as well. JC - virtual bool canClose() { return true; } - - /*virtual*/ void setVisible(bool visible); // do not override - /*virtual*/ void onVisibilityChange ( bool new_visibility ); // do not override - - bool canFocusStealFrontmost() const { return mFocusStealsFrontmost; } - void setFocusStealsFrontmost(bool wants_frontmost) { mFocusStealsFrontmost = wants_frontmost; } - - void setFrontmost(bool take_focus = true, bool restore = true); - virtual void setVisibleAndFrontmost(bool take_focus = true, const LLSD& key = LLSD()); - - // Defaults to false. - virtual bool canSaveAs() const { return false; } - - virtual void saveAs() {} - - void setSnapTarget(LLHandle handle) { mSnappedTo = handle; } - void clearSnapTarget() { mSnappedTo.markDead(); } - LLHandle getSnapTarget() const { return mSnappedTo; } - - LLHandle getHandle() const { return getDerivedHandle(); } - const LLSD& getKey() { return mKey; } - virtual bool matchesKey(const LLSD& key) { return mSingleInstance || KeyCompare::equate(key, mKey); } - - const std::string& getInstanceName() { return mInstanceName; } - - bool isDockable() const { return mCanDock; } - void setCanDock(bool b); - - bool isDocked() const { return mDocked; } - virtual void setDocked(bool docked, bool pop_on_undock = true); - - virtual void setTornOff(bool torn_off) { mTornOff = torn_off; } - bool isTornOff() {return mTornOff;} - void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mPositioning = pos;} - - - // Close the floater returned by getFrontmostClosableFloater() and - // handle refocusing. - static void closeFrontmostFloater(); - - static bool isQuitRequested() { return sQuitting; } - -// LLNotification::Params contextualNotification(const std::string& name) -// { -// return LLNotification::Params(name).context(mNotificationContext); -// } - - static void onClickClose(LLFloater* floater); - static void onClickMinimize(LLFloater* floater); - static void onClickTearOff(LLFloater* floater); - static void onClickDock(LLFloater* floater); - static void onClickHelp(LLFloater* floater); - - static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; } - static LLMultiFloater* getFloaterHost() {return sHostp; } - - void updateTransparency(ETypeTransparency transparency_type); - - void enableResizeCtrls(bool enable, bool width = true, bool height = true); - - bool isPositioning(LLFloaterEnums::EOpenPositioning p) const { return (p == mPositioning); } -protected: - void applyControlsAndPosition(LLFloater* other); - - void stackWith(LLFloater& other); - - virtual void initRectControl(); - virtual bool applyRectControl(); - bool applyDockState(); - void applyPositioning(LLFloater* other, bool on_open); - void applyRelativePosition(); - - void storeRectControl(); - void storeVisibilityControl(); - void storeDockStateControl(); - - void setKey(const LLSD& key); - void setInstanceName(const std::string& name); - - virtual void bringToFront(S32 x, S32 y); - virtual void goneFromFront(); - - void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized - const LLRect& getExpandedRect() const { return mExpandedRect; } - - void setAutoFocus(bool focus) { mAutoFocus = focus; } // whether to automatically take focus when opened - bool getAutoFocus() const { return mAutoFocus; } - LLDragHandle* getDragHandle() const { return mDragHandle; } - - void destroy(); // Don't call this directly. You probably want to call closeFloater() - - virtual void onClickCloseBtn(bool app_quitting = false); - - virtual void updateTitleButtons(); - - // Draws a cone from this floater to parent floater or view (owner) - // Modifies context_cone_opacity (interpolates according to fade time and returns new value) - void drawConeToOwner(F32 &context_cone_opacity, - F32 max_cone_opacity, - LLView *owner_view, - F32 context_fade_time = CONTEXT_CONE_FADE_TIME, - F32 contex_cone_in_alpha = CONTEXT_CONE_IN_ALPHA, - F32 contex_cone_out_alpha = CONTEXT_CONE_OUT_ALPHA); - -private: - void setForeground(bool b); // called only by floaterview - void cleanupHandles(); // remove handles to dead floaters - void createMinimizeButton(); - void buildButtons(const Params& p); - - // Images and tooltips are named in the XML, but we want to look them - // up by index. - static LLUIImage* getButtonImage(const Params& p, EFloaterButton e); - static LLUIImage* getButtonPressedImage(const Params& p, EFloaterButton e); - - /** - * @params is_chrome - if floater is Chrome it means that floater will never get focus. - * Therefore it can't be closed with 'Ctrl+W'. So the tooltip text of close button( X ) - * should be 'Close' not 'Close(Ctrl+W)' as for usual floaters. - */ - static std::string getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome); - - bool offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index); - void addResizeCtrls(); - void layoutResizeCtrls(); - void addDragHandle(); - void layoutDragHandle(); // repair layout - - static void updateActiveFloaterTransparency(); - static void updateInactiveFloaterTransparency(); - void updateTransparency(LLView* view, ETypeTransparency transparency_type); - -public: - static const F32 CONTEXT_CONE_IN_ALPHA; - static const F32 CONTEXT_CONE_OUT_ALPHA; - static const F32 CONTEXT_CONE_FADE_TIME; - - // Called when floater is opened, passes mKey - // Public so external views or floaters can watch for this floater opening - commit_signal_t mOpenSignal; - - // Called when floater is closed, passes app_qitting as LLSD() - // Public so external views or floaters can watch for this floater closing - commit_signal_t mCloseSignal; - - commit_signal_t* mMinimizeSignal; - -protected: - bool mSaveRect; - bool mDefaultRectForGroup; - std::string mRectControl; - std::string mPosXControl; - std::string mPosYControl; - std::string mVisibilityControl; - std::string mDocStateControl; - LLSD mKey; // Key used for retrieving instances; set (for now) by LLFLoaterReg - - LLDragHandle* mDragHandle; - LLResizeBar* mResizeBar[4]; - LLResizeHandle* mResizeHandle[4]; - - LLButton* mButtons[BUTTON_COUNT]; -private: - LLRect mExpandedRect; - - LLUIString mTitle; - LLUIString mShortTitle; - - bool mSingleInstance; // true if there is only ever one instance of the floater - bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it - bool mIsReuseInitialized; // true if mReuseInstance already set from parameters - std::string mInstanceName; // Store the instance name so we can remove ourselves from the list - - bool mCanTearOff; - bool mCanMinimize; - bool mCanClose; - bool mFocusStealsFrontmost = true; // false if we don't want the currently focused floater to cover this floater without user interaction - bool mDragOnLeft; - bool mResizable; - bool mAutoClose; - - LLFloaterEnums::EOpenPositioning mPositioning; - LLCoordFloater mPosition; - - S32 mMinWidth; - S32 mMinHeight; - S32 mHeaderHeight; // height in pixels of header for title, drag bar - S32 mLegacyHeaderHeight;// HACK see initFloaterXML() - - bool mMinimized; - bool mForeground; - LLHandle mDependeeHandle; - - - bool mFirstLook; // true if the _next_ time this floater is visible will be the first time in the session that it is visible. - - typedef std::set > handle_set_t; - typedef std::set >::iterator handle_set_iter_t; - handle_set_t mDependents; - bool mTranslateWithDependents { false }; - - bool mButtonsEnabled[BUTTON_COUNT]; - F32 mButtonScale; - bool mAutoFocus; - LLHandle mSnappedTo; - - LLHandle mHostHandle; - LLHandle mLastHostHandle; - - bool mCanDock; - bool mDocked; - bool mTornOff; - - static LLMultiFloater* sHostp; - static bool sQuitting; - static std::string sButtonNames[BUTTON_COUNT]; - static std::string sButtonToolTips[BUTTON_COUNT]; - static std::string sButtonToolTipsIndex[BUTTON_COUNT]; - - typedef void(*click_callback)(LLFloater*); - static click_callback sButtonCallbacks[BUTTON_COUNT]; - - bool mHasBeenDraggedWhileMinimized; - S32 mPreviousMinimizedBottom; - S32 mPreviousMinimizedLeft; - - F32 mDefaultRelativeX; - F32 mDefaultRelativeY; -}; - - -///////////////////////////////////////////////////////////// -// LLFloaterView -// Parent of all floating panels - -const S32 FLOATER_MIN_VISIBLE_PIXELS = 16; - -class LLFloaterView : public LLUICtrl -{ -public: - struct Params : public LLInitParam::Block{}; - -protected: - LLFloaterView (const Params& p); - friend class LLUICtrlFactory; - -public: - - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - /*virtual*/ void draw(); - /*virtual*/ LLRect getSnapRect() const; - /*virtual*/ void refresh(); - - LLRect findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor ); - - // Given a child of gFloaterView, make sure this view can fit entirely onscreen. - void adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars = false); - - void setMinimizePositionVerticalOffset(S32 offset) { mMinimizePositionVOffset = offset; } - void getMinimizePosition( S32 *left, S32 *bottom); - void restoreAll(); // un-minimize all floaters - typedef std::set skip_list_t; - void pushVisibleAll(bool visible, const skip_list_t& skip_list = skip_list_t()); - void popVisibleAll(const skip_list_t& skip_list = skip_list_t()); - - void setCycleMode(bool mode) { mFocusCycleMode = mode; } - bool getCycleMode() const { return mFocusCycleMode; } - void bringToFront( LLFloater* child, bool give_focus = true, bool restore = true ); - void highlightFocusedFloater(); - void unhighlightFocusedFloater(); - void focusFrontFloater(); - void destroyAllChildren(); - // attempt to close all floaters - void closeAllChildren(bool app_quitting); - bool allChildrenClosed(); - void shiftFloaters(S32 x_offset, S32 y_offset); - - void hideAllFloaters(); - void showHiddenFloaters(); - - - LLFloater* getFrontmost() const; - LLFloater* getBackmost() const; - LLFloater* getParentFloater(LLView* viewp) const; - LLFloater* getFocusedFloater() const; - void syncFloaterTabOrder(); - - // Returns z order of child provided. 0 is closest, larger numbers - // are deeper in the screen. If there is no such child, the return - // value is not defined. - S32 getZOrder(LLFloater* child); - - void setFloaterSnapView(LLHandle snap_view) {mSnapView = snap_view; } - LLFloater* getFrontmostClosableFloater(); - - void setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect); - -private: - void hiddenFloaterClosed(LLFloater* floater); - - LLRect mLastSnapRect; - LLRect mToolbarLeftRect; - LLRect mToolbarBottomRect; - LLRect mToolbarRightRect; - LLHandle mSnapView; - bool mFocusCycleMode; - S32 mSnapOffsetBottom; - S32 mSnapOffsetRight; - S32 mMinimizePositionVOffset; - typedef std::vector, boost::signals2::connection> > hidden_floaters_t; - hidden_floaters_t mHiddenFloaters; - LLHandle mFrontChildHandle; -}; - -// -// Globals -// - -extern LLFloaterView* gFloaterView; - -#endif // LL_FLOATER_H - - - +/** + * @file llfloater.h + * @brief LLFloater base class + * + * $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$ + */ + +// Floating "windows" within the GL display, like the inventory floater, +// mini-map floater, etc. + + +#ifndef LL_FLOATER_H +#define LL_FLOATER_H + +#include "llpanel.h" +#include "lltoolbar.h" +#include "lluuid.h" +//#include "llnotificationsutil.h" +#include +#include + +class LLDragHandle; +class LLResizeHandle; +class LLResizeBar; +class LLButton; +class LLMultiFloater; +class LLFloater; + + +const bool RESIZE_YES = true; +const bool RESIZE_NO = false; + +const bool DRAG_ON_TOP = false; +const bool DRAG_ON_LEFT = true; + +const bool MINIMIZE_YES = true; +const bool MINIMIZE_NO = false; + +const bool CLOSE_YES = true; +const bool CLOSE_NO = false; + +const bool ADJUST_VERTICAL_YES = true; +const bool ADJUST_VERTICAL_NO = false; + +const F32 CONTEXT_CONE_IN_ALPHA = 0.f; +const F32 CONTEXT_CONE_OUT_ALPHA = 1.f; +const F32 CONTEXT_CONE_FADE_TIME = .08f; + +namespace LLFloaterEnums +{ + enum EOpenPositioning + { + POSITIONING_RELATIVE, + POSITIONING_CASCADING, + POSITIONING_CASCADE_GROUP, + POSITIONING_CENTERED, + POSITIONING_SPECIFIED, + POSITIONING_COUNT + }; +} + +namespace LLInitParam +{ + template<> + struct TypeValues : public TypeValuesHelper + { + static void declareValues(); + }; +} + +struct LL_COORD_FLOATER +{ + typedef F32 value_t; + + LLCoordCommon convertToCommon() const; + void convertFromCommon(const LLCoordCommon& from); +protected: + LLHandle mFloater; +}; + +struct LLCoordFloater : LLCoord +{ + typedef LLCoord coord_t; + + LLCoordFloater() {} + LLCoordFloater(F32 x, F32 y, LLFloater& floater); + LLCoordFloater(const LLCoordCommon& other, LLFloater& floater); + + LLCoordFloater& operator=(const LLCoordCommon& other) + { + convertFromCommon(other); + return *this; + } + + LLCoordFloater& operator=(const LLCoordFloater& other); + + bool operator==(const LLCoordFloater& other) const; + bool operator!=(const LLCoordFloater& other) const { return !(*this == other); } + + void setFloater(LLFloater& floater); +}; + +class LLFloater : public LLPanel, public LLInstanceTracker +{ + friend class LLFloaterView; + friend class LLFloaterReg; + friend class LLMultiFloater; + +public: + + struct KeyCompare + { +// static bool compare(const LLSD& a, const LLSD& b); + static bool equate(const LLSD& a, const LLSD& b); +/*==========================================================================*| + bool operator()(const LLSD& a, const LLSD& b) const + { + return compare(a, b); + } +|*==========================================================================*/ + }; + + enum EFloaterButton + { + BUTTON_CLOSE = 0, + BUTTON_RESTORE, + BUTTON_MINIMIZE, + BUTTON_TEAR_OFF, + BUTTON_DOCK, + BUTTON_HELP, + BUTTON_COUNT + }; + + struct Params + : public LLInitParam::Block + { + Optional title, + short_title; + + Optional single_instance, + reuse_instance, + can_resize, + can_minimize, + can_close, + can_drag_on_left, + can_tear_off, + save_rect, + save_visibility, + save_dock_state, + can_dock, + show_title, + auto_close; + + Optional positioning; + + Optional header_height, + legacy_header_height; // HACK see initFromXML() + + Optional rel_x, + rel_y; + + // Images for top-right controls + Optional close_image, + restore_image, + minimize_image, + tear_off_image, + dock_image, + help_image; + Optional close_pressed_image, + restore_pressed_image, + minimize_pressed_image, + tear_off_pressed_image, + dock_pressed_image, + help_pressed_image; + + Optional open_callback, + close_callback; + + Ignored follows; + + Params(); + }; + + // use this to avoid creating your own default LLFloater::Param instance + static const Params& getDefaultParams(); + + // Load translations for tooltips for standard buttons + static void initClass(); + + LLFloater(const LLSD& key, const Params& params = getDefaultParams()); + + virtual ~LLFloater(); + + // Don't export top/left for rect, only height/width + static void setupParamsForExport(Params& p, LLView* parent); + bool buildFromFile(const std::string &filename); + + boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setCloseCallback( const commit_signal_t::slot_type& cb ); + + void initFromParams(const LLFloater::Params& p); + bool initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node = NULL); + + /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); + /*virtual*/ bool canSnapTo(const LLView* other_view); + /*virtual*/ void setSnappedTo(const LLView* snap_view); + /*virtual*/ void setFocus( bool b ); + /*virtual*/ void setIsChrome(bool is_chrome); + /*virtual*/ void setRect(const LLRect &rect); + void setIsSingleInstance(bool is_single_instance); + bool getIsSingleInstance() { return mSingleInstance; } + + void initFloater(const Params& p); + + void openFloater(const LLSD& key = LLSD()); + + // If allowed, close the floater cleanly, releasing focus. + virtual void closeFloater(bool app_quitting = false); + + // Close the floater or its host. Use when hidding or toggling a floater instance. + virtual void closeHostedFloater(); + + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + /*virtual*/ void translate(S32 x, S32 y); + + // Release keyboard and mouse focus + void releaseFocus(); + + // moves to center of gFloaterView + void center(); + + LLMultiFloater* getHost(); + bool isDetachedAndNotMinimized(); + + void applyTitle(); + std::string getCurrentTitle() const; + void setTitle( const std::string& title); + std::string getTitle() const; + void setShortTitle( const std::string& short_title ); + std::string getShortTitle() const; + virtual void setMinimized(bool b); + void moveResizeHandlesToFront(); + void addDependentFloater(LLFloater* dependent, bool reposition = true, bool resize = false); + void addDependentFloater(LLHandle dependent_handle, bool reposition = true, bool resize = false); + LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); } + void removeDependentFloater(LLFloater* dependent); + void fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels); + bool isMinimized() const { return mMinimized; } + /// isShown() differs from getVisible() in that isShown() also considers + /// isMinimized(). isShown() is true only if visible and not minimized. + bool isShown() const; + /// The static isShown() can accept a NULL pointer (which of course + /// returns false). When non-NULL, it calls the non-static isShown(). + static bool isShown(const LLFloater* floater); + static bool isVisible(const LLFloater* floater); + static bool isMinimized(const LLFloater* floater); + bool isFirstLook() { return mFirstLook; } // EXT-2653: This function is necessary to prevent overlapping for secondary showed toasts + virtual bool isFrontmost(); + bool isDependent() { return !mDependeeHandle.isDead(); } + void setCanMinimize(bool can_minimize); + void setCanClose(bool can_close); + void setCanTearOff(bool can_tear_off); + virtual void setCanResize(bool can_resize); + void setCanDrag(bool can_drag); + bool getCanDrag(); + void setHost(LLMultiFloater* host); + bool isResizable() const { return mResizable; } + void setResizeLimits( S32 min_width, S32 min_height ); + void getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; } + + static std::string getControlName(const std::string& name, const LLSD& key); + static LLControlGroup* getControlGroup(); + + bool isMinimizeable() const{ return mCanMinimize; } + bool isCloseable() const{ return mCanClose; } + bool isDragOnLeft() const{ return mDragOnLeft; } + S32 getMinWidth() const{ return mMinWidth; } + S32 getMinHeight() const{ return mMinHeight; } + S32 getHeaderHeight() const { return mHeaderHeight; } + + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); + virtual bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + + virtual bool handleScrollWheel(S32 x, S32 y, S32 mask); + + virtual void draw(); + virtual void drawShadow(LLPanel* panel); + + virtual void onOpen(const LLSD& key) {} + virtual void onClose(bool app_quitting) {} + + // This cannot be "const" until all derived floater canClose() + // methods are const as well. JC + virtual bool canClose() { return true; } + + /*virtual*/ void setVisible(bool visible); // do not override + /*virtual*/ void onVisibilityChange ( bool new_visibility ); // do not override + + bool canFocusStealFrontmost() const { return mFocusStealsFrontmost; } + void setFocusStealsFrontmost(bool wants_frontmost) { mFocusStealsFrontmost = wants_frontmost; } + + void setFrontmost(bool take_focus = true, bool restore = true); + virtual void setVisibleAndFrontmost(bool take_focus = true, const LLSD& key = LLSD()); + + // Defaults to false. + virtual bool canSaveAs() const { return false; } + + virtual void saveAs() {} + + void setSnapTarget(LLHandle handle) { mSnappedTo = handle; } + void clearSnapTarget() { mSnappedTo.markDead(); } + LLHandle getSnapTarget() const { return mSnappedTo; } + + LLHandle getHandle() const { return getDerivedHandle(); } + const LLSD& getKey() { return mKey; } + virtual bool matchesKey(const LLSD& key) { return mSingleInstance || KeyCompare::equate(key, mKey); } + + const std::string& getInstanceName() { return mInstanceName; } + + bool isDockable() const { return mCanDock; } + void setCanDock(bool b); + + bool isDocked() const { return mDocked; } + virtual void setDocked(bool docked, bool pop_on_undock = true); + + virtual void setTornOff(bool torn_off) { mTornOff = torn_off; } + bool isTornOff() {return mTornOff;} + void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mPositioning = pos;} + + + // Close the floater returned by getFrontmostClosableFloater() and + // handle refocusing. + static void closeFrontmostFloater(); + + static bool isQuitRequested() { return sQuitting; } + +// LLNotification::Params contextualNotification(const std::string& name) +// { +// return LLNotification::Params(name).context(mNotificationContext); +// } + + static void onClickClose(LLFloater* floater); + static void onClickMinimize(LLFloater* floater); + static void onClickTearOff(LLFloater* floater); + static void onClickDock(LLFloater* floater); + static void onClickHelp(LLFloater* floater); + + static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; } + static LLMultiFloater* getFloaterHost() {return sHostp; } + + void updateTransparency(ETypeTransparency transparency_type); + + void enableResizeCtrls(bool enable, bool width = true, bool height = true); + + bool isPositioning(LLFloaterEnums::EOpenPositioning p) const { return (p == mPositioning); } +protected: + void applyControlsAndPosition(LLFloater* other); + + void stackWith(LLFloater& other); + + virtual void initRectControl(); + virtual bool applyRectControl(); + bool applyDockState(); + void applyPositioning(LLFloater* other, bool on_open); + void applyRelativePosition(); + + void storeRectControl(); + void storeVisibilityControl(); + void storeDockStateControl(); + + void setKey(const LLSD& key); + void setInstanceName(const std::string& name); + + virtual void bringToFront(S32 x, S32 y); + virtual void goneFromFront(); + + void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized + const LLRect& getExpandedRect() const { return mExpandedRect; } + + void setAutoFocus(bool focus) { mAutoFocus = focus; } // whether to automatically take focus when opened + bool getAutoFocus() const { return mAutoFocus; } + LLDragHandle* getDragHandle() const { return mDragHandle; } + + void destroy(); // Don't call this directly. You probably want to call closeFloater() + + virtual void onClickCloseBtn(bool app_quitting = false); + + virtual void updateTitleButtons(); + + // Draws a cone from this floater to parent floater or view (owner) + // Modifies context_cone_opacity (interpolates according to fade time and returns new value) + void drawConeToOwner(F32 &context_cone_opacity, + F32 max_cone_opacity, + LLView *owner_view, + F32 context_fade_time = CONTEXT_CONE_FADE_TIME, + F32 contex_cone_in_alpha = CONTEXT_CONE_IN_ALPHA, + F32 contex_cone_out_alpha = CONTEXT_CONE_OUT_ALPHA); + +private: + void setForeground(bool b); // called only by floaterview + void cleanupHandles(); // remove handles to dead floaters + void createMinimizeButton(); + void buildButtons(const Params& p); + + // Images and tooltips are named in the XML, but we want to look them + // up by index. + static LLUIImage* getButtonImage(const Params& p, EFloaterButton e); + static LLUIImage* getButtonPressedImage(const Params& p, EFloaterButton e); + + /** + * @params is_chrome - if floater is Chrome it means that floater will never get focus. + * Therefore it can't be closed with 'Ctrl+W'. So the tooltip text of close button( X ) + * should be 'Close' not 'Close(Ctrl+W)' as for usual floaters. + */ + static std::string getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome); + + bool offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index); + void addResizeCtrls(); + void layoutResizeCtrls(); + void addDragHandle(); + void layoutDragHandle(); // repair layout + + static void updateActiveFloaterTransparency(); + static void updateInactiveFloaterTransparency(); + void updateTransparency(LLView* view, ETypeTransparency transparency_type); + +public: + static const F32 CONTEXT_CONE_IN_ALPHA; + static const F32 CONTEXT_CONE_OUT_ALPHA; + static const F32 CONTEXT_CONE_FADE_TIME; + + // Called when floater is opened, passes mKey + // Public so external views or floaters can watch for this floater opening + commit_signal_t mOpenSignal; + + // Called when floater is closed, passes app_qitting as LLSD() + // Public so external views or floaters can watch for this floater closing + commit_signal_t mCloseSignal; + + commit_signal_t* mMinimizeSignal; + +protected: + bool mSaveRect; + bool mDefaultRectForGroup; + std::string mRectControl; + std::string mPosXControl; + std::string mPosYControl; + std::string mVisibilityControl; + std::string mDocStateControl; + LLSD mKey; // Key used for retrieving instances; set (for now) by LLFLoaterReg + + LLDragHandle* mDragHandle; + LLResizeBar* mResizeBar[4]; + LLResizeHandle* mResizeHandle[4]; + + LLButton* mButtons[BUTTON_COUNT]; +private: + LLRect mExpandedRect; + + LLUIString mTitle; + LLUIString mShortTitle; + + bool mSingleInstance; // true if there is only ever one instance of the floater + bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it + bool mIsReuseInitialized; // true if mReuseInstance already set from parameters + std::string mInstanceName; // Store the instance name so we can remove ourselves from the list + + bool mCanTearOff; + bool mCanMinimize; + bool mCanClose; + bool mFocusStealsFrontmost = true; // false if we don't want the currently focused floater to cover this floater without user interaction + bool mDragOnLeft; + bool mResizable; + bool mAutoClose; + + LLFloaterEnums::EOpenPositioning mPositioning; + LLCoordFloater mPosition; + + S32 mMinWidth; + S32 mMinHeight; + S32 mHeaderHeight; // height in pixels of header for title, drag bar + S32 mLegacyHeaderHeight;// HACK see initFloaterXML() + + bool mMinimized; + bool mForeground; + LLHandle mDependeeHandle; + + + bool mFirstLook; // true if the _next_ time this floater is visible will be the first time in the session that it is visible. + + typedef std::set > handle_set_t; + typedef std::set >::iterator handle_set_iter_t; + handle_set_t mDependents; + bool mTranslateWithDependents { false }; + + bool mButtonsEnabled[BUTTON_COUNT]; + F32 mButtonScale; + bool mAutoFocus; + LLHandle mSnappedTo; + + LLHandle mHostHandle; + LLHandle mLastHostHandle; + + bool mCanDock; + bool mDocked; + bool mTornOff; + + static LLMultiFloater* sHostp; + static bool sQuitting; + static std::string sButtonNames[BUTTON_COUNT]; + static std::string sButtonToolTips[BUTTON_COUNT]; + static std::string sButtonToolTipsIndex[BUTTON_COUNT]; + + typedef void(*click_callback)(LLFloater*); + static click_callback sButtonCallbacks[BUTTON_COUNT]; + + bool mHasBeenDraggedWhileMinimized; + S32 mPreviousMinimizedBottom; + S32 mPreviousMinimizedLeft; + + F32 mDefaultRelativeX; + F32 mDefaultRelativeY; +}; + + +///////////////////////////////////////////////////////////// +// LLFloaterView +// Parent of all floating panels + +const S32 FLOATER_MIN_VISIBLE_PIXELS = 16; + +class LLFloaterView : public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block{}; + +protected: + LLFloaterView (const Params& p); + friend class LLUICtrlFactory; + +public: + + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + /*virtual*/ void draw(); + /*virtual*/ LLRect getSnapRect() const; + /*virtual*/ void refresh(); + + LLRect findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor ); + + // Given a child of gFloaterView, make sure this view can fit entirely onscreen. + void adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars = false); + + void setMinimizePositionVerticalOffset(S32 offset) { mMinimizePositionVOffset = offset; } + void getMinimizePosition( S32 *left, S32 *bottom); + void restoreAll(); // un-minimize all floaters + typedef std::set skip_list_t; + void pushVisibleAll(bool visible, const skip_list_t& skip_list = skip_list_t()); + void popVisibleAll(const skip_list_t& skip_list = skip_list_t()); + + void setCycleMode(bool mode) { mFocusCycleMode = mode; } + bool getCycleMode() const { return mFocusCycleMode; } + void bringToFront( LLFloater* child, bool give_focus = true, bool restore = true ); + void highlightFocusedFloater(); + void unhighlightFocusedFloater(); + void focusFrontFloater(); + void destroyAllChildren(); + // attempt to close all floaters + void closeAllChildren(bool app_quitting); + bool allChildrenClosed(); + void shiftFloaters(S32 x_offset, S32 y_offset); + + void hideAllFloaters(); + void showHiddenFloaters(); + + + LLFloater* getFrontmost() const; + LLFloater* getBackmost() const; + LLFloater* getParentFloater(LLView* viewp) const; + LLFloater* getFocusedFloater() const; + void syncFloaterTabOrder(); + + // Returns z order of child provided. 0 is closest, larger numbers + // are deeper in the screen. If there is no such child, the return + // value is not defined. + S32 getZOrder(LLFloater* child); + + void setFloaterSnapView(LLHandle snap_view) {mSnapView = snap_view; } + LLFloater* getFrontmostClosableFloater(); + + void setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect); + +private: + void hiddenFloaterClosed(LLFloater* floater); + + LLRect mLastSnapRect; + LLRect mToolbarLeftRect; + LLRect mToolbarBottomRect; + LLRect mToolbarRightRect; + LLHandle mSnapView; + bool mFocusCycleMode; + S32 mSnapOffsetBottom; + S32 mSnapOffsetRight; + S32 mMinimizePositionVOffset; + typedef std::vector, boost::signals2::connection> > hidden_floaters_t; + hidden_floaters_t mHiddenFloaters; + LLHandle mFrontChildHandle; +}; + +// +// Globals +// + +extern LLFloaterView* gFloaterView; + +#endif // LL_FLOATER_H + + + diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index fad1155d90..fd5a370bc3 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -1,609 +1,609 @@ -/** - * @file llfloaterreg.cpp - * @brief LLFloaterReg Floater Registration Class - * - * $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$ - */ - -#include "linden_common.h" - -#include "llfloaterreg.h" - -//#include "llagent.h" -#include "llfloater.h" -#include "llmultifloater.h" -#include "llfloaterreglistener.h" -#include "lluiusage.h" - -//******************************************************* - -//static -LLFloaterReg::instance_list_t LLFloaterReg::sNullInstanceList; -LLFloaterReg::instance_map_t LLFloaterReg::sInstanceMap; -LLFloaterReg::build_map_t LLFloaterReg::sBuildMap; -std::map LLFloaterReg::sGroupMap; -bool LLFloaterReg::sBlockShowFloaters = false; -std::set LLFloaterReg::sAlwaysShowableList; - -static LLFloaterRegListener sFloaterRegListener; - -//******************************************************* - -//static -void LLFloaterReg::add(const std::string& name, const std::string& filename, const LLFloaterBuildFunc& func, const std::string& groupname) -{ - sBuildMap[name].mFunc = func; - sBuildMap[name].mFile = filename; - sGroupMap[name] = groupname.empty() ? name : groupname; - sGroupMap[groupname] = groupname; // for referencing directly by group name -} - -//static -bool LLFloaterReg::isRegistered(const std::string& name) -{ - return sBuildMap.find(name) != sBuildMap.end(); -} - -//static -LLFloater* LLFloaterReg::getLastFloaterInGroup(const std::string& name) -{ - const std::string& groupname = sGroupMap[name]; - if (!groupname.empty()) - { - instance_list_t& list = sInstanceMap[groupname]; - if (!list.empty()) - { - for (instance_list_t::reverse_iterator iter = list.rbegin(); iter != list.rend(); ++iter) - { - LLFloater* inst = *iter; - - if (inst->getVisible() && !inst->isMinimized()) - { - return inst; - } - } - } - } - return NULL; -} - -LLFloater* LLFloaterReg::getLastFloaterCascading() -{ - LLRect candidate_rect; - candidate_rect.mTop = 100000; - LLFloater* candidate_floater = NULL; - - std::map::const_iterator it = sGroupMap.begin(), it_end = sGroupMap.end(); - for( ; it != it_end; ++it) - { - const std::string& group_name = it->second; - - instance_list_t& instances = sInstanceMap[group_name]; - - for (instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); ++iter) - { - LLFloater* inst = *iter; - - if (inst->getVisible() - && (inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADING) - || inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADE_GROUP))) - { - if (candidate_rect.mTop > inst->getRect().mTop) - { - candidate_floater = inst; - candidate_rect = inst->getRect(); - } - } - } - } - - return candidate_floater; -} - -//static -LLFloater* LLFloaterReg::findInstance(const std::string& name, const LLSD& key) -{ - LLFloater* res = NULL; - const std::string& groupname = sGroupMap[name]; - if (!groupname.empty()) - { - instance_list_t& list = sInstanceMap[groupname]; - for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) - { - LLFloater* inst = *iter; - if (inst->matchesKey(key)) - { - res = inst; - break; - } - } - } - return res; -} - -//static -LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key) -{ - LLFloater* res = findInstance(name, key); - if (!res) - { - const LLFloaterBuildFunc& build_func = sBuildMap[name].mFunc; - const std::string& xui_file = sBuildMap[name].mFile; - if (build_func) - { - const std::string& groupname = sGroupMap[name]; - if (!groupname.empty()) - { - instance_list_t& list = sInstanceMap[groupname]; - - res = build_func(key); - if (!res) - { - LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL; - return NULL; - } - bool success = res->buildFromFile(xui_file); - if (!success) - { - LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL; - return NULL; - } - - // Note: key should eventually be a non optional LLFloater arg; for now, set mKey to be safe - if (res->mKey.isUndefined()) - { - res->mKey = key; - } - res->setInstanceName(name); - - LLFloater *last_floater = (list.empty() ? NULL : list.back()); - - res->applyControlsAndPosition(last_floater); - - gFloaterView->adjustToFitScreen(res, false); - - list.push_back(res); - } - } - if (!res) - { - LL_WARNS() << "Floater type: '" << name << "' not registered." << LL_ENDL; - } - } - return res; -} - -//static -LLFloater* LLFloaterReg::removeInstance(const std::string& name, const LLSD& key) -{ - LLFloater* res = NULL; - const std::string& groupname = sGroupMap[name]; - if (!groupname.empty()) - { - instance_list_t& list = sInstanceMap[groupname]; - for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) - { - LLFloater* inst = *iter; - if (inst->matchesKey(key)) - { - res = inst; - list.erase(iter); - break; - } - } - } - return res; -} - -//static -// returns true if the instance existed -bool LLFloaterReg::destroyInstance(const std::string& name, const LLSD& key) -{ - LLFloater* inst = removeInstance(name, key); - if (inst) - { - delete inst; - return true; - } - else - { - return false; - } -} - -// Iterators -//static -LLFloaterReg::const_instance_list_t& LLFloaterReg::getFloaterList(const std::string& name) -{ - instance_map_t::iterator iter = sInstanceMap.find(name); - if (iter != sInstanceMap.end()) - { - return iter->second; - } - else - { - return sNullInstanceList; - } -} - -// Visibility Management - -//static -LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, bool focus) -{ - if( sBlockShowFloaters - // see EXT-7090 - && sAlwaysShowableList.find(name) == sAlwaysShowableList.end()) - return 0;// - LLFloater* instance = getInstance(name, key); - if (instance) - { - instance->openFloater(key); - if (focus) - instance->setFocus(true); - } - return instance; -} - -//static -// returns true if the instance exists -bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key) -{ - LLFloater* instance = findInstance(name, key); - if (instance) - { - instance->closeHostedFloater(); - } - return (instance != NULL); -} - -//static -// returns true if the instance is visible when completed -bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key) -{ - LLFloater* instance = findInstance(name, key); - if (instance && instance->isShown()) - { - instance->closeHostedFloater(); - return false; - } - - instance = showInstance(name, key, true); - - return instance != nullptr; -} - -//static -// returns true if the instance exists and is visible (doesnt matter minimized or not) -bool LLFloaterReg::instanceVisible(const std::string& name, const LLSD& key) -{ - LLFloater* instance = findInstance(name, key); - return LLFloater::isVisible(instance); -} - -//static -void LLFloaterReg::showInitialVisibleInstances() -{ - // Iterate through alll registered instance names and show any with a save visible state - for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter) - { - const std::string& name = iter->first; - std::string controlname = getVisibilityControlName(name); - if (LLFloater::getControlGroup()->controlExists(controlname)) - { - bool isvis = LLFloater::getControlGroup()->getBOOL(controlname); - if (isvis) - { - showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true - } - } - } -} - -//static -void LLFloaterReg::hideVisibleInstances(const std::set& exceptions) -{ - // Iterate through alll active instances and hide them - for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter) - { - const std::string& name = iter->first; - if (exceptions.find(name) != exceptions.end()) - continue; - instance_list_t& list = iter->second; - for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) - { - LLFloater* floater = *iter; - floater->pushVisible(false); - } - } -} - -//static -void LLFloaterReg::restoreVisibleInstances() -{ - // Iterate through all active instances and restore visibility - for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter) - { - instance_list_t& list = iter->second; - for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) - { - LLFloater* floater = *iter; - floater->popVisible(); - } - } -} - -//static -std::string LLFloaterReg::getRectControlName(const std::string& name) -{ - return std::string("floater_rect_") + getBaseControlName(name); -} - -//static -std::string LLFloaterReg::declareRectControl(const std::string& name) -{ - std::string controlname = getRectControlName(name); - LLFloater::getControlGroup()->declareRect(controlname, LLRect(), - llformat("Window Size for %s", name.c_str()), - LLControlVariable::PERSIST_NONDFT); - return controlname; -} - -std::string LLFloaterReg::declarePosXControl(const std::string& name) -{ - std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_x"; - LLFloater::getControlGroup()->declareF32(controlname, - 10.f, - llformat("Window X Position for %s", name.c_str()), - LLControlVariable::PERSIST_NONDFT); - return controlname; -} - -std::string LLFloaterReg::declarePosYControl(const std::string& name) -{ - std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_y"; - LLFloater::getControlGroup()->declareF32(controlname, - 10.f, - llformat("Window Y Position for %s", name.c_str()), - LLControlVariable::PERSIST_NONDFT); - - return controlname; -} - - -//static -std::string LLFloaterReg::getVisibilityControlName(const std::string& name) -{ - return std::string("floater_vis_") + getBaseControlName(name); -} - -//static -std::string LLFloaterReg::getBaseControlName(const std::string& name) -{ - std::string res(name); - LLStringUtil::replaceChar( res, ' ', '_' ); - return res; -} - - -//static -std::string LLFloaterReg::declareVisibilityControl(const std::string& name) -{ - std::string controlname = getVisibilityControlName(name); - LLFloater::getControlGroup()->declareBOOL(controlname, false, - llformat("Window Visibility for %s", name.c_str()), - LLControlVariable::PERSIST_NONDFT); - return controlname; -} - -//static -std::string LLFloaterReg::declareDockStateControl(const std::string& name) -{ - std::string controlname = getDockStateControlName(name); - LLFloater::getControlGroup()->declareBOOL(controlname, true, - llformat("Window Docking state for %s", name.c_str()), - LLControlVariable::PERSIST_NONDFT); - return controlname; - -} - -//static -std::string LLFloaterReg::getDockStateControlName(const std::string& name) -{ - std::string res = std::string("floater_dock_") + name; - LLStringUtil::replaceChar( res, ' ', '_' ); - return res; -} - - -//static -void LLFloaterReg::registerControlVariables() -{ - // Iterate through alll registered instance names and register rect and visibility control variables - for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter) - { - const std::string& name = iter->first; - if (LLFloater::getControlGroup()->controlExists(getRectControlName(name))) - { - declareRectControl(name); - } - if (LLFloater::getControlGroup()->controlExists(getVisibilityControlName(name))) - { - declareVisibilityControl(name); - } - } - - const LLSD& exclude_list = LLUI::getInstance()->mSettingGroups["config"]->getLLSD("always_showable_floaters"); - for (LLSD::array_const_iterator iter = exclude_list.beginArray(); - iter != exclude_list.endArray(); - iter++) - { - sAlwaysShowableList.insert(iter->asString()); - } -} - -//static -void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key) -{ - // - // Floaters controlled by the toolbar behave a bit differently from others. - // Namely they have 3-4 states as defined in the design wiki page here: - // https://wiki.lindenlab.com/wiki/FUI_Button_states - // - // The basic idea is this: - // * If the target floater is minimized, this button press will un-minimize it. - // * Else if the target floater is closed open it. - // * Else if the target floater does not have focus, give it focus. - // * Also, if it is not on top, bring it forward when focus is given. - // * Else the target floater is open, close it. - // - std::string name = sdname.asString(); - LLFloater* instance = getInstance(name, key); - - if (!instance) - { - LL_DEBUGS() << "Unable to get instance of floater '" << name << "'" << LL_ENDL; - return; - } - - // If hosted, we need to take that into account - LLFloater* host = instance->getHost(); - - if (host) - { - if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) - { - host->setMinimized(false); - instance->openFloater(key); - instance->setVisibleAndFrontmost(true, key); - } - else if (!instance->getVisible()) - { - instance->openFloater(key); - instance->setVisibleAndFrontmost(true, key); - instance->setFocus(true); - } - else - { - instance->closeHostedFloater(); - } - } - else - { - if (instance->isMinimized()) - { - instance->setMinimized(false); - instance->setVisibleAndFrontmost(true, key); - } - else if (!instance->isShown()) - { - instance->openFloater(key); - instance->setVisibleAndFrontmost(true, key); - } - else if (!instance->isFrontmost()) - { - instance->setVisibleAndFrontmost(true, key); - } - else - { - instance->closeHostedFloater(); - } - } -} - -// static -// Same as toggleInstanceOrBringToFront but does not close floater. -// unlike showInstance() does not trigger onOpen() if already open -void LLFloaterReg::showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key) -{ - std::string name = sdname.asString(); - LLFloater* instance = getInstance(name, key); - - - if (!instance) - { - LL_DEBUGS() << "Unable to get instance of floater '" << name << "'" << LL_ENDL; - return; - } - - // If hosted, we need to take that into account - LLFloater* host = instance->getHost(); - - if (host) - { - if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) - { - host->setMinimized(false); - instance->openFloater(key); - instance->setVisibleAndFrontmost(true, key); - } - else if (!instance->getVisible()) - { - instance->openFloater(key); - instance->setVisibleAndFrontmost(true, key); - instance->setFocus(true); - } - } - else - { - if (instance->isMinimized()) - { - instance->setMinimized(false); - instance->setVisibleAndFrontmost(true, key); - } - else if (!instance->isShown()) - { - instance->openFloater(key); - instance->setVisibleAndFrontmost(true, key); - } - else if (!instance->isFrontmost()) - { - instance->setVisibleAndFrontmost(true, key); - } - } -} - -// static -U32 LLFloaterReg::getVisibleFloaterInstanceCount() -{ - U32 count = 0; - - std::map::const_iterator it = sGroupMap.begin(), it_end = sGroupMap.end(); - for( ; it != it_end; ++it) - { - const std::string& group_name = it->second; - - instance_list_t& instances = sInstanceMap[group_name]; - - for (instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); ++iter) - { - LLFloater* inst = *iter; - - if (inst->getVisible() && !inst->isMinimized()) - { - count++; - } - } - } - - return count; -} +/** + * @file llfloaterreg.cpp + * @brief LLFloaterReg Floater Registration Class + * + * $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$ + */ + +#include "linden_common.h" + +#include "llfloaterreg.h" + +//#include "llagent.h" +#include "llfloater.h" +#include "llmultifloater.h" +#include "llfloaterreglistener.h" +#include "lluiusage.h" + +//******************************************************* + +//static +LLFloaterReg::instance_list_t LLFloaterReg::sNullInstanceList; +LLFloaterReg::instance_map_t LLFloaterReg::sInstanceMap; +LLFloaterReg::build_map_t LLFloaterReg::sBuildMap; +std::map LLFloaterReg::sGroupMap; +bool LLFloaterReg::sBlockShowFloaters = false; +std::set LLFloaterReg::sAlwaysShowableList; + +static LLFloaterRegListener sFloaterRegListener; + +//******************************************************* + +//static +void LLFloaterReg::add(const std::string& name, const std::string& filename, const LLFloaterBuildFunc& func, const std::string& groupname) +{ + sBuildMap[name].mFunc = func; + sBuildMap[name].mFile = filename; + sGroupMap[name] = groupname.empty() ? name : groupname; + sGroupMap[groupname] = groupname; // for referencing directly by group name +} + +//static +bool LLFloaterReg::isRegistered(const std::string& name) +{ + return sBuildMap.find(name) != sBuildMap.end(); +} + +//static +LLFloater* LLFloaterReg::getLastFloaterInGroup(const std::string& name) +{ + const std::string& groupname = sGroupMap[name]; + if (!groupname.empty()) + { + instance_list_t& list = sInstanceMap[groupname]; + if (!list.empty()) + { + for (instance_list_t::reverse_iterator iter = list.rbegin(); iter != list.rend(); ++iter) + { + LLFloater* inst = *iter; + + if (inst->getVisible() && !inst->isMinimized()) + { + return inst; + } + } + } + } + return NULL; +} + +LLFloater* LLFloaterReg::getLastFloaterCascading() +{ + LLRect candidate_rect; + candidate_rect.mTop = 100000; + LLFloater* candidate_floater = NULL; + + std::map::const_iterator it = sGroupMap.begin(), it_end = sGroupMap.end(); + for( ; it != it_end; ++it) + { + const std::string& group_name = it->second; + + instance_list_t& instances = sInstanceMap[group_name]; + + for (instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); ++iter) + { + LLFloater* inst = *iter; + + if (inst->getVisible() + && (inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADING) + || inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADE_GROUP))) + { + if (candidate_rect.mTop > inst->getRect().mTop) + { + candidate_floater = inst; + candidate_rect = inst->getRect(); + } + } + } + } + + return candidate_floater; +} + +//static +LLFloater* LLFloaterReg::findInstance(const std::string& name, const LLSD& key) +{ + LLFloater* res = NULL; + const std::string& groupname = sGroupMap[name]; + if (!groupname.empty()) + { + instance_list_t& list = sInstanceMap[groupname]; + for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) + { + LLFloater* inst = *iter; + if (inst->matchesKey(key)) + { + res = inst; + break; + } + } + } + return res; +} + +//static +LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key) +{ + LLFloater* res = findInstance(name, key); + if (!res) + { + const LLFloaterBuildFunc& build_func = sBuildMap[name].mFunc; + const std::string& xui_file = sBuildMap[name].mFile; + if (build_func) + { + const std::string& groupname = sGroupMap[name]; + if (!groupname.empty()) + { + instance_list_t& list = sInstanceMap[groupname]; + + res = build_func(key); + if (!res) + { + LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL; + return NULL; + } + bool success = res->buildFromFile(xui_file); + if (!success) + { + LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL; + return NULL; + } + + // Note: key should eventually be a non optional LLFloater arg; for now, set mKey to be safe + if (res->mKey.isUndefined()) + { + res->mKey = key; + } + res->setInstanceName(name); + + LLFloater *last_floater = (list.empty() ? NULL : list.back()); + + res->applyControlsAndPosition(last_floater); + + gFloaterView->adjustToFitScreen(res, false); + + list.push_back(res); + } + } + if (!res) + { + LL_WARNS() << "Floater type: '" << name << "' not registered." << LL_ENDL; + } + } + return res; +} + +//static +LLFloater* LLFloaterReg::removeInstance(const std::string& name, const LLSD& key) +{ + LLFloater* res = NULL; + const std::string& groupname = sGroupMap[name]; + if (!groupname.empty()) + { + instance_list_t& list = sInstanceMap[groupname]; + for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) + { + LLFloater* inst = *iter; + if (inst->matchesKey(key)) + { + res = inst; + list.erase(iter); + break; + } + } + } + return res; +} + +//static +// returns true if the instance existed +bool LLFloaterReg::destroyInstance(const std::string& name, const LLSD& key) +{ + LLFloater* inst = removeInstance(name, key); + if (inst) + { + delete inst; + return true; + } + else + { + return false; + } +} + +// Iterators +//static +LLFloaterReg::const_instance_list_t& LLFloaterReg::getFloaterList(const std::string& name) +{ + instance_map_t::iterator iter = sInstanceMap.find(name); + if (iter != sInstanceMap.end()) + { + return iter->second; + } + else + { + return sNullInstanceList; + } +} + +// Visibility Management + +//static +LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, bool focus) +{ + if( sBlockShowFloaters + // see EXT-7090 + && sAlwaysShowableList.find(name) == sAlwaysShowableList.end()) + return 0;// + LLFloater* instance = getInstance(name, key); + if (instance) + { + instance->openFloater(key); + if (focus) + instance->setFocus(true); + } + return instance; +} + +//static +// returns true if the instance exists +bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key) +{ + LLFloater* instance = findInstance(name, key); + if (instance) + { + instance->closeHostedFloater(); + } + return (instance != NULL); +} + +//static +// returns true if the instance is visible when completed +bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key) +{ + LLFloater* instance = findInstance(name, key); + if (instance && instance->isShown()) + { + instance->closeHostedFloater(); + return false; + } + + instance = showInstance(name, key, true); + + return instance != nullptr; +} + +//static +// returns true if the instance exists and is visible (doesnt matter minimized or not) +bool LLFloaterReg::instanceVisible(const std::string& name, const LLSD& key) +{ + LLFloater* instance = findInstance(name, key); + return LLFloater::isVisible(instance); +} + +//static +void LLFloaterReg::showInitialVisibleInstances() +{ + // Iterate through alll registered instance names and show any with a save visible state + for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter) + { + const std::string& name = iter->first; + std::string controlname = getVisibilityControlName(name); + if (LLFloater::getControlGroup()->controlExists(controlname)) + { + bool isvis = LLFloater::getControlGroup()->getBOOL(controlname); + if (isvis) + { + showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true + } + } + } +} + +//static +void LLFloaterReg::hideVisibleInstances(const std::set& exceptions) +{ + // Iterate through alll active instances and hide them + for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter) + { + const std::string& name = iter->first; + if (exceptions.find(name) != exceptions.end()) + continue; + instance_list_t& list = iter->second; + for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) + { + LLFloater* floater = *iter; + floater->pushVisible(false); + } + } +} + +//static +void LLFloaterReg::restoreVisibleInstances() +{ + // Iterate through all active instances and restore visibility + for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter) + { + instance_list_t& list = iter->second; + for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter) + { + LLFloater* floater = *iter; + floater->popVisible(); + } + } +} + +//static +std::string LLFloaterReg::getRectControlName(const std::string& name) +{ + return std::string("floater_rect_") + getBaseControlName(name); +} + +//static +std::string LLFloaterReg::declareRectControl(const std::string& name) +{ + std::string controlname = getRectControlName(name); + LLFloater::getControlGroup()->declareRect(controlname, LLRect(), + llformat("Window Size for %s", name.c_str()), + LLControlVariable::PERSIST_NONDFT); + return controlname; +} + +std::string LLFloaterReg::declarePosXControl(const std::string& name) +{ + std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_x"; + LLFloater::getControlGroup()->declareF32(controlname, + 10.f, + llformat("Window X Position for %s", name.c_str()), + LLControlVariable::PERSIST_NONDFT); + return controlname; +} + +std::string LLFloaterReg::declarePosYControl(const std::string& name) +{ + std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_y"; + LLFloater::getControlGroup()->declareF32(controlname, + 10.f, + llformat("Window Y Position for %s", name.c_str()), + LLControlVariable::PERSIST_NONDFT); + + return controlname; +} + + +//static +std::string LLFloaterReg::getVisibilityControlName(const std::string& name) +{ + return std::string("floater_vis_") + getBaseControlName(name); +} + +//static +std::string LLFloaterReg::getBaseControlName(const std::string& name) +{ + std::string res(name); + LLStringUtil::replaceChar( res, ' ', '_' ); + return res; +} + + +//static +std::string LLFloaterReg::declareVisibilityControl(const std::string& name) +{ + std::string controlname = getVisibilityControlName(name); + LLFloater::getControlGroup()->declareBOOL(controlname, false, + llformat("Window Visibility for %s", name.c_str()), + LLControlVariable::PERSIST_NONDFT); + return controlname; +} + +//static +std::string LLFloaterReg::declareDockStateControl(const std::string& name) +{ + std::string controlname = getDockStateControlName(name); + LLFloater::getControlGroup()->declareBOOL(controlname, true, + llformat("Window Docking state for %s", name.c_str()), + LLControlVariable::PERSIST_NONDFT); + return controlname; + +} + +//static +std::string LLFloaterReg::getDockStateControlName(const std::string& name) +{ + std::string res = std::string("floater_dock_") + name; + LLStringUtil::replaceChar( res, ' ', '_' ); + return res; +} + + +//static +void LLFloaterReg::registerControlVariables() +{ + // Iterate through alll registered instance names and register rect and visibility control variables + for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter) + { + const std::string& name = iter->first; + if (LLFloater::getControlGroup()->controlExists(getRectControlName(name))) + { + declareRectControl(name); + } + if (LLFloater::getControlGroup()->controlExists(getVisibilityControlName(name))) + { + declareVisibilityControl(name); + } + } + + const LLSD& exclude_list = LLUI::getInstance()->mSettingGroups["config"]->getLLSD("always_showable_floaters"); + for (LLSD::array_const_iterator iter = exclude_list.beginArray(); + iter != exclude_list.endArray(); + iter++) + { + sAlwaysShowableList.insert(iter->asString()); + } +} + +//static +void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key) +{ + // + // Floaters controlled by the toolbar behave a bit differently from others. + // Namely they have 3-4 states as defined in the design wiki page here: + // https://wiki.lindenlab.com/wiki/FUI_Button_states + // + // The basic idea is this: + // * If the target floater is minimized, this button press will un-minimize it. + // * Else if the target floater is closed open it. + // * Else if the target floater does not have focus, give it focus. + // * Also, if it is not on top, bring it forward when focus is given. + // * Else the target floater is open, close it. + // + std::string name = sdname.asString(); + LLFloater* instance = getInstance(name, key); + + if (!instance) + { + LL_DEBUGS() << "Unable to get instance of floater '" << name << "'" << LL_ENDL; + return; + } + + // If hosted, we need to take that into account + LLFloater* host = instance->getHost(); + + if (host) + { + if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) + { + host->setMinimized(false); + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->getVisible()) + { + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + instance->setFocus(true); + } + else + { + instance->closeHostedFloater(); + } + } + else + { + if (instance->isMinimized()) + { + instance->setMinimized(false); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isShown()) + { + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isFrontmost()) + { + instance->setVisibleAndFrontmost(true, key); + } + else + { + instance->closeHostedFloater(); + } + } +} + +// static +// Same as toggleInstanceOrBringToFront but does not close floater. +// unlike showInstance() does not trigger onOpen() if already open +void LLFloaterReg::showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key) +{ + std::string name = sdname.asString(); + LLFloater* instance = getInstance(name, key); + + + if (!instance) + { + LL_DEBUGS() << "Unable to get instance of floater '" << name << "'" << LL_ENDL; + return; + } + + // If hosted, we need to take that into account + LLFloater* host = instance->getHost(); + + if (host) + { + if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) + { + host->setMinimized(false); + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->getVisible()) + { + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + instance->setFocus(true); + } + } + else + { + if (instance->isMinimized()) + { + instance->setMinimized(false); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isShown()) + { + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isFrontmost()) + { + instance->setVisibleAndFrontmost(true, key); + } + } +} + +// static +U32 LLFloaterReg::getVisibleFloaterInstanceCount() +{ + U32 count = 0; + + std::map::const_iterator it = sGroupMap.begin(), it_end = sGroupMap.end(); + for( ; it != it_end; ++it) + { + const std::string& group_name = it->second; + + instance_list_t& instances = sInstanceMap[group_name]; + + for (instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); ++iter) + { + LLFloater* inst = *iter; + + if (inst->getVisible() && !inst->isMinimized()) + { + count++; + } + } + } + + return count; +} diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index 988372e8fe..6a642cbb27 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -1,158 +1,158 @@ -/** - * @file llfloaterreg.h - * @brief LLFloaterReg Floater Registration Class - * - * $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 LLFLOATERREG_H -#define LLFLOATERREG_H - -/// llcommon -#include "llrect.h" -#include "llsd.h" - -#include -#include - -//******************************************************* -// -// Floater Class Registry -// - -class LLFloater; -class LLUICtrl; - -typedef boost::function LLFloaterBuildFunc; - -class LLFloaterReg -{ -public: - // We use a list of LLFloater's instead of a set for two reasons: - // 1) With a list we have a predictable ordering, useful for finding the last opened floater of a given type. - // 2) We can change the key of a floater without altering the list. - typedef std::list instance_list_t; - typedef const instance_list_t const_instance_list_t; - typedef std::map instance_map_t; - - struct BuildData - { - LLFloaterBuildFunc mFunc; - std::string mFile; - }; - typedef std::map build_map_t; - -private: - friend class LLFloaterRegListener; - static instance_list_t sNullInstanceList; - static instance_map_t sInstanceMap; - static build_map_t sBuildMap; - static std::map sGroupMap; - static bool sBlockShowFloaters; - /** - * Defines list of floater names that can be shown despite state of sBlockShowFloaters. - */ - static std::set sAlwaysShowableList; - -public: - // Registration - - // usage: LLFloaterClassRegistry::add("foo", (LLFloaterBuildFunc)&LLFloaterClassRegistry::build); - template - static LLFloater* build(const LLSD& key) - { - T* floater = new T(key); - return floater; - } - - static void add(const std::string& name, const std::string& file, const LLFloaterBuildFunc& func, - const std::string& groupname = LLStringUtil::null); - static bool isRegistered(const std::string& name); - - // Helpers - static LLFloater* getLastFloaterInGroup(const std::string& name); - static LLFloater* getLastFloaterCascading(); - - // Find / get (create) / remove / destroy - static LLFloater* findInstance(const std::string& name, const LLSD& key = LLSD()); - static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD()); - static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD()); - static bool destroyInstance(const std::string& name, const LLSD& key = LLSD()); - - // Iterators - static const_instance_list_t& getFloaterList(const std::string& name); - - // Visibility Management - // return NULL if instance not found or can't create instance (no builder) - static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false); - // Close a floater (may destroy or set invisible) - // return false if can't find instance - static bool hideInstance(const std::string& name, const LLSD& key = LLSD()); - // return true if instance is visible: - static bool toggleInstance(const std::string& name, const LLSD& key = LLSD()); - static bool instanceVisible(const std::string& name, const LLSD& key = LLSD()); - - static void showInitialVisibleInstances(); - static void hideVisibleInstances(const std::set& exceptions = std::set()); - static void restoreVisibleInstances(); - - // Control Variables - static std::string getRectControlName(const std::string& name); - static std::string declareRectControl(const std::string& name); - static std::string declarePosXControl(const std::string& name); - static std::string declarePosYControl(const std::string& name); - static std::string getVisibilityControlName(const std::string& name); - static std::string declareVisibilityControl(const std::string& name); - static std::string getBaseControlName(const std::string& name); - static std::string declareDockStateControl(const std::string& name); - static std::string getDockStateControlName(const std::string& name); - - static void registerControlVariables(); - - // Callback wrappers - static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); - static void showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); - - // Typed find / get / show - template - static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD()) - { - return dynamic_cast(findInstance(name, key)); - } - - template - static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD()) - { - return dynamic_cast(getInstance(name, key)); - } - - template - static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false) - { - return dynamic_cast(showInstance(name, key, focus)); - } - - static void blockShowFloaters(bool value) { sBlockShowFloaters = value;} - - static U32 getVisibleFloaterInstanceCount(); -}; - -#endif +/** + * @file llfloaterreg.h + * @brief LLFloaterReg Floater Registration Class + * + * $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 LLFLOATERREG_H +#define LLFLOATERREG_H + +/// llcommon +#include "llrect.h" +#include "llsd.h" + +#include +#include + +//******************************************************* +// +// Floater Class Registry +// + +class LLFloater; +class LLUICtrl; + +typedef boost::function LLFloaterBuildFunc; + +class LLFloaterReg +{ +public: + // We use a list of LLFloater's instead of a set for two reasons: + // 1) With a list we have a predictable ordering, useful for finding the last opened floater of a given type. + // 2) We can change the key of a floater without altering the list. + typedef std::list instance_list_t; + typedef const instance_list_t const_instance_list_t; + typedef std::map instance_map_t; + + struct BuildData + { + LLFloaterBuildFunc mFunc; + std::string mFile; + }; + typedef std::map build_map_t; + +private: + friend class LLFloaterRegListener; + static instance_list_t sNullInstanceList; + static instance_map_t sInstanceMap; + static build_map_t sBuildMap; + static std::map sGroupMap; + static bool sBlockShowFloaters; + /** + * Defines list of floater names that can be shown despite state of sBlockShowFloaters. + */ + static std::set sAlwaysShowableList; + +public: + // Registration + + // usage: LLFloaterClassRegistry::add("foo", (LLFloaterBuildFunc)&LLFloaterClassRegistry::build); + template + static LLFloater* build(const LLSD& key) + { + T* floater = new T(key); + return floater; + } + + static void add(const std::string& name, const std::string& file, const LLFloaterBuildFunc& func, + const std::string& groupname = LLStringUtil::null); + static bool isRegistered(const std::string& name); + + // Helpers + static LLFloater* getLastFloaterInGroup(const std::string& name); + static LLFloater* getLastFloaterCascading(); + + // Find / get (create) / remove / destroy + static LLFloater* findInstance(const std::string& name, const LLSD& key = LLSD()); + static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD()); + static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD()); + static bool destroyInstance(const std::string& name, const LLSD& key = LLSD()); + + // Iterators + static const_instance_list_t& getFloaterList(const std::string& name); + + // Visibility Management + // return NULL if instance not found or can't create instance (no builder) + static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false); + // Close a floater (may destroy or set invisible) + // return false if can't find instance + static bool hideInstance(const std::string& name, const LLSD& key = LLSD()); + // return true if instance is visible: + static bool toggleInstance(const std::string& name, const LLSD& key = LLSD()); + static bool instanceVisible(const std::string& name, const LLSD& key = LLSD()); + + static void showInitialVisibleInstances(); + static void hideVisibleInstances(const std::set& exceptions = std::set()); + static void restoreVisibleInstances(); + + // Control Variables + static std::string getRectControlName(const std::string& name); + static std::string declareRectControl(const std::string& name); + static std::string declarePosXControl(const std::string& name); + static std::string declarePosYControl(const std::string& name); + static std::string getVisibilityControlName(const std::string& name); + static std::string declareVisibilityControl(const std::string& name); + static std::string getBaseControlName(const std::string& name); + static std::string declareDockStateControl(const std::string& name); + static std::string getDockStateControlName(const std::string& name); + + static void registerControlVariables(); + + // Callback wrappers + static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); + static void showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); + + // Typed find / get / show + template + static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD()) + { + return dynamic_cast(findInstance(name, key)); + } + + template + static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD()) + { + return dynamic_cast(getInstance(name, key)); + } + + template + static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false) + { + return dynamic_cast(showInstance(name, key, focus)); + } + + static void blockShowFloaters(bool value) { sBlockShowFloaters = value;} + + static U32 getVisibleFloaterInstanceCount(); +}; + +#endif diff --git a/indra/llui/llflyoutbutton.cpp b/indra/llui/llflyoutbutton.cpp index 1682e195df..2e198e8dbc 100644 --- a/indra/llui/llflyoutbutton.cpp +++ b/indra/llui/llflyoutbutton.cpp @@ -1,77 +1,77 @@ -/** - * @file llflyoutbutton.cpp - * @brief LLFlyoutButton base class - * - * $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 "linden_common.h" - -// file includes -#include "llflyoutbutton.h" - -//static LLDefaultChildRegistry::Register r2("flyout_button"); - -const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24; - -LLFlyoutButton::LLFlyoutButton(const Params& p) -: LLComboBox(p), - mToggleState(false), - mActionButton(NULL) -{ - // Always use text box - // Text label button - LLButton::Params bp(p.action_button); - bp.name(p.label); - bp.label(p.label); - bp.rect.left(0).bottom(0).width(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH).height(getRect().getHeight()); - bp.click_callback.function(boost::bind(&LLFlyoutButton::onActionButtonClick, this, _2)); - bp.follows.flags(FOLLOWS_ALL); - - mActionButton = LLUICtrlFactory::create(bp); - addChild(mActionButton); -} - -void LLFlyoutButton::onActionButtonClick(const LLSD& data) -{ - // remember last list selection? - mList->deselect(); - onCommit(); -} - -void LLFlyoutButton::draw() -{ - mActionButton->setToggleState(mToggleState); - mButton->setToggleState(mToggleState); - - //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or - // the label reflects the last selected item, for now we have to manually remove the label - setLabel(LLStringUtil::null); - LLComboBox::draw(); -} - -void LLFlyoutButton::setToggleState(bool state) -{ - mToggleState = state; -} - - +/** + * @file llflyoutbutton.cpp + * @brief LLFlyoutButton base class + * + * $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 "linden_common.h" + +// file includes +#include "llflyoutbutton.h" + +//static LLDefaultChildRegistry::Register r2("flyout_button"); + +const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24; + +LLFlyoutButton::LLFlyoutButton(const Params& p) +: LLComboBox(p), + mToggleState(false), + mActionButton(NULL) +{ + // Always use text box + // Text label button + LLButton::Params bp(p.action_button); + bp.name(p.label); + bp.label(p.label); + bp.rect.left(0).bottom(0).width(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH).height(getRect().getHeight()); + bp.click_callback.function(boost::bind(&LLFlyoutButton::onActionButtonClick, this, _2)); + bp.follows.flags(FOLLOWS_ALL); + + mActionButton = LLUICtrlFactory::create(bp); + addChild(mActionButton); +} + +void LLFlyoutButton::onActionButtonClick(const LLSD& data) +{ + // remember last list selection? + mList->deselect(); + onCommit(); +} + +void LLFlyoutButton::draw() +{ + mActionButton->setToggleState(mToggleState); + mButton->setToggleState(mToggleState); + + //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or + // the label reflects the last selected item, for now we have to manually remove the label + setLabel(LLStringUtil::null); + LLComboBox::draw(); +} + +void LLFlyoutButton::setToggleState(bool state) +{ + mToggleState = state; +} + + diff --git a/indra/llui/llflyoutbutton.h b/indra/llui/llflyoutbutton.h index 3992bb1e01..7a49501318 100644 --- a/indra/llui/llflyoutbutton.h +++ b/indra/llui/llflyoutbutton.h @@ -1,68 +1,68 @@ -/** - * @file llflyoutbutton.h - * @brief LLFlyoutButton base class - * - * $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$ - */ - -// A control that displays the name of the chosen item, which when clicked -// shows a scrolling box of choices. - -#ifndef LL_LLFLYOUTBUTTON_H -#define LL_LLFLYOUTBUTTON_H - -#include "llcombobox.h" - -// Classes - -class LLFlyoutButton : public LLComboBox -{ -public: - struct Params : public LLInitParam::Block - { - Optional action_button; - Deprecated allow_text_entry; - - Params() - : action_button("action_button"), - allow_text_entry("allow_text_entry") - { - changeDefault(LLComboBox::Params::allow_text_entry, false); - } - - }; -protected: - LLFlyoutButton(const Params&); - friend class LLUICtrlFactory; -public: - virtual void draw(); - - void setToggleState(bool state); - - void onActionButtonClick(const LLSD& data); - -protected: - LLButton* mActionButton; - bool mToggleState; -}; - -#endif // LL_LLFLYOUTBUTTON_H +/** + * @file llflyoutbutton.h + * @brief LLFlyoutButton base class + * + * $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$ + */ + +// A control that displays the name of the chosen item, which when clicked +// shows a scrolling box of choices. + +#ifndef LL_LLFLYOUTBUTTON_H +#define LL_LLFLYOUTBUTTON_H + +#include "llcombobox.h" + +// Classes + +class LLFlyoutButton : public LLComboBox +{ +public: + struct Params : public LLInitParam::Block + { + Optional action_button; + Deprecated allow_text_entry; + + Params() + : action_button("action_button"), + allow_text_entry("allow_text_entry") + { + changeDefault(LLComboBox::Params::allow_text_entry, false); + } + + }; +protected: + LLFlyoutButton(const Params&); + friend class LLUICtrlFactory; +public: + virtual void draw(); + + void setToggleState(bool state); + + void onActionButtonClick(const LLSD& data); + +protected: + LLButton* mActionButton; + bool mToggleState; +}; + +#endif // LL_LLFLYOUTBUTTON_H diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 02d02a15f4..937dde4def 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -1,509 +1,509 @@ -/** - * @file llfocusmgr.cpp - * @brief LLFocusMgr base class - * - * $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$ - */ - -#include "linden_common.h" - -#include "llfocusmgr.h" -#include "lluictrl.h" -#include "v4color.h" - -const F32 FOCUS_FADE_TIME = 0.3f; - -LLFocusableElement::LLFocusableElement() -: mFocusLostCallback(NULL), - mFocusReceivedCallback(NULL), - mFocusChangedCallback(NULL), - mTopLostCallback(NULL) -{ -} - -// virtual -bool LLFocusableElement::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - return false; -} - -// virtual -bool LLFocusableElement::handleKeyUp(KEY key, MASK mask, bool called_from_parent) -{ - return false; -} - -// virtual -bool LLFocusableElement::handleUnicodeChar(llwchar uni_char, bool called_from_parent) -{ - return false; -} - -// virtual -bool LLFocusableElement::wantsKeyUpKeyDown() const -{ - return false; -} - -//virtual -bool LLFocusableElement::wantsReturnKey() const -{ - return false; -} - -// virtual -LLFocusableElement::~LLFocusableElement() -{ - delete mFocusLostCallback; - delete mFocusReceivedCallback; - delete mFocusChangedCallback; - delete mTopLostCallback; -} - -void LLFocusableElement::onFocusReceived() -{ - if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this); - if (mFocusChangedCallback) (*mFocusChangedCallback)(this); -} - -void LLFocusableElement::onFocusLost() -{ - if (mFocusLostCallback) (*mFocusLostCallback)(this); - if (mFocusChangedCallback) (*mFocusChangedCallback)(this); -} - -void LLFocusableElement::onTopLost() -{ - if (mTopLostCallback) (*mTopLostCallback)(this); -} - -bool LLFocusableElement::hasFocus() const -{ - return gFocusMgr.getKeyboardFocus() == this; -} - -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 ), - mMouseCaptor( NULL ), - mKeyboardFocus( NULL ), - mLastKeyboardFocus( NULL ), - mDefaultKeyboardFocus( NULL ), - mKeystrokesOnly(false), - mTopCtrl( NULL ), - 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( LLView* view ) -{ - if( childHasMouseCapture( view ) ) - { - setMouseCapture( NULL ); - } - - if( childHasKeyboardFocus( view )) - { - if (view == mLockedView) - { - mLockedView = NULL; - setKeyboardFocus( NULL ); - } - else - { - setKeyboardFocus( mLockedView ); - } - } - - LLUI::getInstance()->removePopup(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 - && dynamic_cast(new_focus) - && !dynamic_cast(new_focus)->hasAncestor(mLockedView)))) - { - // don't allow focus to go to anything that is not the locked focus - // or one of its descendants - return; - } - - mKeystrokesOnly = keystrokes_only; - - if( new_focus != mKeyboardFocus ) - { - mLastKeyboardFocus = mKeyboardFocus; - mKeyboardFocus = new_focus; - - // 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()) - { - new_focus_list.push_back(ctrl->getHandle()); - } - - // 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()) - { - new_focus_list.pop_back(); - old_focus_list.pop_back(); - } - - // 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. - if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) - { - mDefaultKeyboardFocus->setFocus(true); - } - - LLView* focus_subtree = dynamic_cast(mKeyboardFocus); - LLView* viewp = dynamic_cast(mKeyboardFocus); - // find root-most focus root - while(viewp) - { - if (viewp->isFocusRoot()) - { - focus_subtree = viewp; - } - viewp = viewp->getParent(); - } - - - if (focus_subtree) - { - LLView* focused_view = dynamic_cast(mKeyboardFocus); - mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle(); - } - } - - if (lock) - { - lockFocus(); - } - - focus_dirty = true; -} - - -// Returns true is parent or any descedent of parent has keyboard focus. -bool LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const -{ - LLView* focus_view = dynamic_cast(mKeyboardFocus); - while( focus_view ) - { - if( focus_view == parent ) - { - return true; - } - focus_view = focus_view->getParent(); - } - return false; -} - -// Returns true is parent or any descedent of parent is the mouse captor. -bool LLFocusMgr::childHasMouseCapture( const LLView* parent ) const -{ - if( mMouseCaptor && dynamic_cast(mMouseCaptor) != NULL ) - { - LLView* captor_view = (LLView*)mMouseCaptor; - while( captor_view ) - { - if( captor_view == parent ) - { - return true; - } - captor_view = captor_view->getParent(); - } - } - return false; -} - -void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ) -{ - // should be ok to unlock here, as you have to know the locked view - // in order to unlock it - if (focus == mLockedView) - { - mLockedView = NULL; - } - - if( mKeyboardFocus == focus ) - { - mKeyboardFocus = NULL; - } -} - -bool LLFocusMgr::keyboardFocusHasAccelerators() const -{ - LLView* focus_view = dynamic_cast(mKeyboardFocus); - while( focus_view ) - { - if(focus_view->hasAccelerators()) - { - return true; - } - - focus_view = focus_view->getParent(); - } - return false; -} - -void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor ) -{ - if( new_captor != mMouseCaptor ) - { - LLMouseHandler* old_captor = mMouseCaptor; - mMouseCaptor = new_captor; - - if (LLView::sDebugMouseHandling) - { - if (new_captor) - { - LL_INFOS() << "New mouse captor: " << new_captor->getName() << LL_ENDL; - } - else - { - LL_INFOS() << "New mouse captor: NULL" << LL_ENDL; - } - } - - if( old_captor ) - { - old_captor->onMouseCaptureLost(); - } - - } -} - -void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ) -{ - if( mMouseCaptor == captor ) - { - mMouseCaptor = NULL; - } -} - - -bool LLFocusMgr::childIsTopCtrl( const LLView* parent ) const -{ - LLView* top_view = (LLView*)mTopCtrl; - while( top_view ) - { - if( top_view == parent ) - { - return true; - } - top_view = top_view->getParent(); - } - return false; -} - - - -// set new_top = NULL to release top_view. -void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) -{ - LLUICtrl* old_top = mTopCtrl; - if( new_top != old_top ) - { - mTopCtrl = new_top; - - if (old_top) - { - old_top->onTopLost(); - } - } -} - -void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) -{ - if( mTopCtrl == top_view ) - { - mTopCtrl = NULL; - } -} - -void LLFocusMgr::lockFocus() -{ - mLockedView = dynamic_cast(mKeyboardFocus); -} - -void LLFocusMgr::unlockFocus() -{ - mLockedView = NULL; -} - -F32 LLFocusMgr::getFocusFlashAmt() const -{ - return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f); -} - -LLColor4 LLFocusMgr::getFocusColor() const -{ - static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor"); - 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) - { - focus_color.mV[VALPHA] *= 0.4f; - } - return focus_color; -} - -void LLFocusMgr::triggerFocusFlash() -{ - mFocusFlashTimer.reset(); -} - -void LLFocusMgr::setAppHasFocus(bool focus) -{ - if (!mAppHasFocus && focus) - { - triggerFocusFlash(); - } - - // release focus from "top ctrl"s, which generally hides them - if (!focus) - { - LLUI::getInstance()->clearPopups(); - } - mAppHasFocus = focus; -} - -LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const -{ - if (subtree_root) - { - 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 found_it->second.get(); - } - } - return NULL; -} - -void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root) -{ - if (subtree_root) - { - mImpl->mFocusHistory.erase(subtree_root->getHandle()); - } -} +/** + * @file llfocusmgr.cpp + * @brief LLFocusMgr base class + * + * $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$ + */ + +#include "linden_common.h" + +#include "llfocusmgr.h" +#include "lluictrl.h" +#include "v4color.h" + +const F32 FOCUS_FADE_TIME = 0.3f; + +LLFocusableElement::LLFocusableElement() +: mFocusLostCallback(NULL), + mFocusReceivedCallback(NULL), + mFocusChangedCallback(NULL), + mTopLostCallback(NULL) +{ +} + +// virtual +bool LLFocusableElement::handleKey(KEY key, MASK mask, bool called_from_parent) +{ + return false; +} + +// virtual +bool LLFocusableElement::handleKeyUp(KEY key, MASK mask, bool called_from_parent) +{ + return false; +} + +// virtual +bool LLFocusableElement::handleUnicodeChar(llwchar uni_char, bool called_from_parent) +{ + return false; +} + +// virtual +bool LLFocusableElement::wantsKeyUpKeyDown() const +{ + return false; +} + +//virtual +bool LLFocusableElement::wantsReturnKey() const +{ + return false; +} + +// virtual +LLFocusableElement::~LLFocusableElement() +{ + delete mFocusLostCallback; + delete mFocusReceivedCallback; + delete mFocusChangedCallback; + delete mTopLostCallback; +} + +void LLFocusableElement::onFocusReceived() +{ + if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this); + if (mFocusChangedCallback) (*mFocusChangedCallback)(this); +} + +void LLFocusableElement::onFocusLost() +{ + if (mFocusLostCallback) (*mFocusLostCallback)(this); + if (mFocusChangedCallback) (*mFocusChangedCallback)(this); +} + +void LLFocusableElement::onTopLost() +{ + if (mTopLostCallback) (*mTopLostCallback)(this); +} + +bool LLFocusableElement::hasFocus() const +{ + return gFocusMgr.getKeyboardFocus() == this; +} + +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 ), + mMouseCaptor( NULL ), + mKeyboardFocus( NULL ), + mLastKeyboardFocus( NULL ), + mDefaultKeyboardFocus( NULL ), + mKeystrokesOnly(false), + mTopCtrl( NULL ), + 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( LLView* view ) +{ + if( childHasMouseCapture( view ) ) + { + setMouseCapture( NULL ); + } + + if( childHasKeyboardFocus( view )) + { + if (view == mLockedView) + { + mLockedView = NULL; + setKeyboardFocus( NULL ); + } + else + { + setKeyboardFocus( mLockedView ); + } + } + + LLUI::getInstance()->removePopup(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 + && dynamic_cast(new_focus) + && !dynamic_cast(new_focus)->hasAncestor(mLockedView)))) + { + // don't allow focus to go to anything that is not the locked focus + // or one of its descendants + return; + } + + mKeystrokesOnly = keystrokes_only; + + if( new_focus != mKeyboardFocus ) + { + mLastKeyboardFocus = mKeyboardFocus; + mKeyboardFocus = new_focus; + + // 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()) + { + new_focus_list.push_back(ctrl->getHandle()); + } + + // 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()) + { + new_focus_list.pop_back(); + old_focus_list.pop_back(); + } + + // 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. + if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) + { + mDefaultKeyboardFocus->setFocus(true); + } + + LLView* focus_subtree = dynamic_cast(mKeyboardFocus); + LLView* viewp = dynamic_cast(mKeyboardFocus); + // find root-most focus root + while(viewp) + { + if (viewp->isFocusRoot()) + { + focus_subtree = viewp; + } + viewp = viewp->getParent(); + } + + + if (focus_subtree) + { + LLView* focused_view = dynamic_cast(mKeyboardFocus); + mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle(); + } + } + + if (lock) + { + lockFocus(); + } + + focus_dirty = true; +} + + +// Returns true is parent or any descedent of parent has keyboard focus. +bool LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const +{ + LLView* focus_view = dynamic_cast(mKeyboardFocus); + while( focus_view ) + { + if( focus_view == parent ) + { + return true; + } + focus_view = focus_view->getParent(); + } + return false; +} + +// Returns true is parent or any descedent of parent is the mouse captor. +bool LLFocusMgr::childHasMouseCapture( const LLView* parent ) const +{ + if( mMouseCaptor && dynamic_cast(mMouseCaptor) != NULL ) + { + LLView* captor_view = (LLView*)mMouseCaptor; + while( captor_view ) + { + if( captor_view == parent ) + { + return true; + } + captor_view = captor_view->getParent(); + } + } + return false; +} + +void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ) +{ + // should be ok to unlock here, as you have to know the locked view + // in order to unlock it + if (focus == mLockedView) + { + mLockedView = NULL; + } + + if( mKeyboardFocus == focus ) + { + mKeyboardFocus = NULL; + } +} + +bool LLFocusMgr::keyboardFocusHasAccelerators() const +{ + LLView* focus_view = dynamic_cast(mKeyboardFocus); + while( focus_view ) + { + if(focus_view->hasAccelerators()) + { + return true; + } + + focus_view = focus_view->getParent(); + } + return false; +} + +void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor ) +{ + if( new_captor != mMouseCaptor ) + { + LLMouseHandler* old_captor = mMouseCaptor; + mMouseCaptor = new_captor; + + if (LLView::sDebugMouseHandling) + { + if (new_captor) + { + LL_INFOS() << "New mouse captor: " << new_captor->getName() << LL_ENDL; + } + else + { + LL_INFOS() << "New mouse captor: NULL" << LL_ENDL; + } + } + + if( old_captor ) + { + old_captor->onMouseCaptureLost(); + } + + } +} + +void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ) +{ + if( mMouseCaptor == captor ) + { + mMouseCaptor = NULL; + } +} + + +bool LLFocusMgr::childIsTopCtrl( const LLView* parent ) const +{ + LLView* top_view = (LLView*)mTopCtrl; + while( top_view ) + { + if( top_view == parent ) + { + return true; + } + top_view = top_view->getParent(); + } + return false; +} + + + +// set new_top = NULL to release top_view. +void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) +{ + LLUICtrl* old_top = mTopCtrl; + if( new_top != old_top ) + { + mTopCtrl = new_top; + + if (old_top) + { + old_top->onTopLost(); + } + } +} + +void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) +{ + if( mTopCtrl == top_view ) + { + mTopCtrl = NULL; + } +} + +void LLFocusMgr::lockFocus() +{ + mLockedView = dynamic_cast(mKeyboardFocus); +} + +void LLFocusMgr::unlockFocus() +{ + mLockedView = NULL; +} + +F32 LLFocusMgr::getFocusFlashAmt() const +{ + return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f); +} + +LLColor4 LLFocusMgr::getFocusColor() const +{ + static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor"); + 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) + { + focus_color.mV[VALPHA] *= 0.4f; + } + return focus_color; +} + +void LLFocusMgr::triggerFocusFlash() +{ + mFocusFlashTimer.reset(); +} + +void LLFocusMgr::setAppHasFocus(bool focus) +{ + if (!mAppHasFocus && focus) + { + triggerFocusFlash(); + } + + // release focus from "top ctrl"s, which generally hides them + if (!focus) + { + LLUI::getInstance()->clearPopups(); + } + mAppHasFocus = focus; +} + +LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const +{ + if (subtree_root) + { + 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 found_it->second.get(); + } + } + return NULL; +} + +void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root) +{ + if (subtree_root) + { + mImpl->mFocusHistory.erase(subtree_root->getHandle()); + } +} diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 6bdd3e8e9a..1fa0ac137e 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -1,160 +1,160 @@ -/** - * @file llfocusmgr.h - * @brief LLFocusMgr base class - * - * $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$ - */ - -// Singleton that manages keyboard and mouse focus - -#ifndef LL_LLFOCUSMGR_H -#define LL_LLFOCUSMGR_H - -#include "llstring.h" -#include "llframetimer.h" -#include "llui.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 -public: - LLFocusableElement(); - virtual ~LLFocusableElement(); - - virtual void setFocus( bool b ); - virtual bool hasFocus() const; - - 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 handleKeyUp(KEY key, MASK mask, bool called_from_parent); - virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); - - /** - * If true this LLFocusableElement wants to receive KEYUP and KEYDOWN messages - * even for normal character strokes. - * Default implementation returns false. - */ - virtual bool wantsKeyUpKeyDown() const; - virtual bool wantsReturnKey() const; - - virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere -protected: - virtual void onFocusReceived(); - virtual void onFocusLost(); - focus_signal_t* mFocusLostCallback; - focus_signal_t* mFocusReceivedCallback; - focus_signal_t* mFocusChangedCallback; - focus_signal_t* mTopLostCallback; -}; - - -class LLFocusMgr -{ -public: - LLFocusMgr(); - ~LLFocusMgr(); - - // Mouse Captor - void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse. - LLMouseHandler* getMouseCapture() const { return mMouseCaptor; } - void removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ); - bool childHasMouseCapture( const LLView* parent ) const; - - // Keyboard Focus - void setKeyboardFocus(LLFocusableElement* new_focus, bool lock = false, bool keystrokes_only = false); // new_focus = NULL to release the focus. - LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; } - LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; } - bool childHasKeyboardFocus( const LLView* parent ) const; - void removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ); - bool getKeystrokesOnly() { return mKeystrokesOnly; } - void setKeystrokesOnly(bool keystrokes_only) { mKeystrokesOnly = keystrokes_only; } - - F32 getFocusFlashAmt() const; - S32 getFocusFlashWidth() const { return ll_round(lerp(1.f, 3.f, getFocusFlashAmt())); } - LLColor4 getFocusColor() const; - void triggerFocusFlash(); - bool getAppHasFocus() const { return mAppHasFocus; } - void setAppHasFocus(bool focus); - LLView* getLastFocusForGroup(LLView* subtree_root) const; - void clearLastFocusForGroup(LLView* subtree_root); - - // If setKeyboardFocus(NULL) is called, and there is a non-NULL default - // keyboard focus view, focus goes there. JC - void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; } - LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; } - - - // Top View - void setTopCtrl(LLUICtrl* new_top); - LLUICtrl* getTopCtrl() const { return mTopCtrl; } - void removeTopCtrlWithoutCallback( const LLUICtrl* top_view ); - bool childIsTopCtrl( const LLView* parent ) const; - - // All Three - void releaseFocusIfNeeded( LLView* top_view ); - void lockFocus(); - void unlockFocus(); - bool focusLocked() const { return mLockedView != NULL; } - - bool keyboardFocusHasAccelerators() const; - - struct Impl; - -private: - LLUICtrl* mLockedView; - - // Mouse Captor - LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object - - // Keyboard Focus - LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object - LLFocusableElement* mLastKeyboardFocus; // who last had focus - LLFocusableElement* mDefaultKeyboardFocus; - bool mKeystrokesOnly; - - // Top View - LLUICtrl* mTopCtrl; - - LLFrameTimer mFocusFlashTimer; - - bool mAppHasFocus; - - Impl * mImpl; -}; - -extern LLFocusMgr gFocusMgr; - -#endif // LL_LLFOCUSMGR_H - - +/** + * @file llfocusmgr.h + * @brief LLFocusMgr base class + * + * $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$ + */ + +// Singleton that manages keyboard and mouse focus + +#ifndef LL_LLFOCUSMGR_H +#define LL_LLFOCUSMGR_H + +#include "llstring.h" +#include "llframetimer.h" +#include "llui.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 +public: + LLFocusableElement(); + virtual ~LLFocusableElement(); + + virtual void setFocus( bool b ); + virtual bool hasFocus() const; + + 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 handleKeyUp(KEY key, MASK mask, bool called_from_parent); + virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); + + /** + * If true this LLFocusableElement wants to receive KEYUP and KEYDOWN messages + * even for normal character strokes. + * Default implementation returns false. + */ + virtual bool wantsKeyUpKeyDown() const; + virtual bool wantsReturnKey() const; + + virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere +protected: + virtual void onFocusReceived(); + virtual void onFocusLost(); + focus_signal_t* mFocusLostCallback; + focus_signal_t* mFocusReceivedCallback; + focus_signal_t* mFocusChangedCallback; + focus_signal_t* mTopLostCallback; +}; + + +class LLFocusMgr +{ +public: + LLFocusMgr(); + ~LLFocusMgr(); + + // Mouse Captor + void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse. + LLMouseHandler* getMouseCapture() const { return mMouseCaptor; } + void removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ); + bool childHasMouseCapture( const LLView* parent ) const; + + // Keyboard Focus + void setKeyboardFocus(LLFocusableElement* new_focus, bool lock = false, bool keystrokes_only = false); // new_focus = NULL to release the focus. + LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; } + LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; } + bool childHasKeyboardFocus( const LLView* parent ) const; + void removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ); + bool getKeystrokesOnly() { return mKeystrokesOnly; } + void setKeystrokesOnly(bool keystrokes_only) { mKeystrokesOnly = keystrokes_only; } + + F32 getFocusFlashAmt() const; + S32 getFocusFlashWidth() const { return ll_round(lerp(1.f, 3.f, getFocusFlashAmt())); } + LLColor4 getFocusColor() const; + void triggerFocusFlash(); + bool getAppHasFocus() const { return mAppHasFocus; } + void setAppHasFocus(bool focus); + LLView* getLastFocusForGroup(LLView* subtree_root) const; + void clearLastFocusForGroup(LLView* subtree_root); + + // If setKeyboardFocus(NULL) is called, and there is a non-NULL default + // keyboard focus view, focus goes there. JC + void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; } + LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; } + + + // Top View + void setTopCtrl(LLUICtrl* new_top); + LLUICtrl* getTopCtrl() const { return mTopCtrl; } + void removeTopCtrlWithoutCallback( const LLUICtrl* top_view ); + bool childIsTopCtrl( const LLView* parent ) const; + + // All Three + void releaseFocusIfNeeded( LLView* top_view ); + void lockFocus(); + void unlockFocus(); + bool focusLocked() const { return mLockedView != NULL; } + + bool keyboardFocusHasAccelerators() const; + + struct Impl; + +private: + LLUICtrl* mLockedView; + + // Mouse Captor + LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object + + // Keyboard Focus + LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object + LLFocusableElement* mLastKeyboardFocus; // who last had focus + LLFocusableElement* mDefaultKeyboardFocus; + bool mKeystrokesOnly; + + // Top View + LLUICtrl* mTopCtrl; + + LLFrameTimer mFocusFlashTimer; + + bool mAppHasFocus; + + Impl * mImpl; +}; + +extern LLFocusMgr gFocusMgr; + +#endif // LL_LLFOCUSMGR_H + + diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index c01e811477..47821e38ca 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -1,2129 +1,2129 @@ -/** - * @file llfolderview.cpp - * @brief Implementation of the folder view collection of classes. - * - * $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 "linden_common.h" - -#include "llfolderview.h" -#include "llfolderviewmodel.h" -#include "llclipboard.h" // *TODO: remove this once hack below gone. -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llmenugl.h" -#include "llpanel.h" -#include "llscrollcontainer.h" // hack to allow scrolling -#include "lltextbox.h" -#include "lltrans.h" -#include "llui.h" -#include "lluictrlfactory.h" - -// Linden library includes -#include "lldbstrings.h" -#include "llfocusmgr.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llrender.h" - -// Third-party library includes -#include - -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- - -const S32 RENAME_HEIGHT_PAD = 1; -const S32 AUTO_OPEN_STACK_DEPTH = 16; - -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, - SIGNAL_KEYBOARD_FOCUS = 2 -}; - -F32 LLFolderView::sAutoOpenTime = 1.f; - -//--------------------------------------------------------------------------- - -// Tells all folders in a folderview to close themselves -// For efficiency, calls setOpenArrangeRecursively(). -// The calling function must then call: -// LLFolderView* root = getRoot(); -// if( root ) -// { -// root->arrange( NULL, NULL ); -// root->scrollToShowSelection(); -// } -// to patch things up. -class LLCloseAllFoldersFunctor : public LLFolderViewFunctor -{ -public: - LLCloseAllFoldersFunctor(bool close) { mOpen = !close; } - virtual ~LLCloseAllFoldersFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - bool mOpen; -}; - - -void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) -{ - folder->setOpenArrangeRecursively(mOpen); -} - -// Do nothing. -void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) -{ } - -//--------------------------------------------------------------------------- - -void LLAllDescendentsPassedFilter::doFolder(LLFolderViewFolder* folder) -{ - mAllDescendentsPassedFilter &= (folder) && (folder->passedFilter()) && (folder->descendantsPassedFilter()); -} - -void LLAllDescendentsPassedFilter::doItem(LLFolderViewItem* item) -{ - mAllDescendentsPassedFilter &= (item) && (item->passedFilter()); -} - -///---------------------------------------------------------------------------- -/// Class LLFolderViewScrollContainer -///---------------------------------------------------------------------------- - -// virtual -const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const -{ - LLRect rect = LLRect::null; - if (mScrolledView) - { - LLFolderView* folder_view = dynamic_cast(mScrolledView); - if (folder_view) - { - S32 height = folder_view->getRect().getHeight(); - - rect = mScrolledView->getRect(); - rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height); - } - } - - return rect; -} - -LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer::Params& p) -: LLScrollContainer(p) -{} - -///---------------------------------------------------------------------------- -/// Class LLFolderView -///---------------------------------------------------------------------------- -LLFolderView::Params::Params() -: title("title"), - use_label_suffix("use_label_suffix"), - allow_multiselect("allow_multiselect", true), - allow_drag("allow_drag", true), - show_empty_message("show_empty_message", true), - suppress_folder_menu("suppress_folder_menu", false), - use_ellipses("use_ellipses", false), - options_menu("options_menu", "") -{ - folder_indentation = -4; -} - - -// Default constructor -LLFolderView::LLFolderView(const Params& p) -: LLFolderViewFolder(p), - mScrollContainer( NULL ), - mPopupMenuHandle(), - mMenuFileName(p.options_menu), - mAllowMultiSelect(p.allow_multiselect), - mAllowDrag(p.allow_drag), - mShowEmptyMessage(p.show_empty_message), - mShowFolderHierarchy(false), - mRenameItem( NULL ), - mNeedsScroll( false ), - mUseLabelSuffix(p.use_label_suffix), - mSuppressFolderMenu(p.suppress_folder_menu), - mPinningSelectedItem(false), - mNeedsAutoSelect( false ), - mAutoSelectOverride(false), - mNeedsAutoRename(false), - mShowSelectionContext(false), - mShowSingleSelection(false), - mArrangeGeneration(0), - mSignalSelectCallback(0), - mMinWidth(0), - mDragAndDropThisFrame(false), - mCallbackRegistrar(NULL), - mEnableRegistrar(NULL), - mUseEllipses(p.use_ellipses), - mDraggingOverItem(NULL), - mStatusTextBox(NULL), - mShowItemLinkOverlays(p.show_item_link_overlays), - mViewModel(p.view_model), - mGroupedItemModel(p.grouped_item_model), - mForceArrange(false), - mSingleFolderMode(false) -{ - LLPanel* panel = p.parent_panel; - mParentPanel = panel->getHandle(); - mViewModel->setFolderView(this); - mRoot = this; - - LLRect rect = p.rect; - LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); - setRect( rect ); - reshape(rect.getWidth(), rect.getHeight()); - mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); - mKeyboardSelection = false; - mIndentation = getParentFolder() ? getParentFolder()->getIndentation() + mLocalIndentation : 0; - - //clear label - // go ahead and render root folder as usual - // just make sure the label ("Inventory Folder") never shows up - mLabel = LLStringUtil::null; - - // Escape is handled by reverting the rename, not commiting it (default behavior) - LLLineEditor::Params params; - params.name("ren"); - params.rect(rect); - params.font(getLabelFontForStyle(LLFontGL::NORMAL)); - params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN); - params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2)); - params.prevalidator(&LLTextValidate::validateASCIIPrintableNoPipe); - params.commit_on_focus_lost(true); - params.visible(false); - mRenamer = LLUICtrlFactory::create (params); - addChild(mRenamer); - - // Textbox - LLTextBox::Params text_p; - LLFontGL* font = getLabelFontForStyle(mLabelStyle); - //mIconPad, mTextPad are set in folder_view_item.xml - LLRect new_r = LLRect(rect.mLeft + mIconPad, - rect.mTop - mTextPad, - rect.mRight, - rect.mTop - mTextPad - font->getLineHeight()); - text_p.rect(new_r); - text_p.name(std::string(p.name)); - text_p.font(font); - text_p.visible(false); - text_p.parse_urls(true); - text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047 - // set text padding the same as in People panel. EXT-7047, EXT-4837 - text_p.h_pad(STATUS_TEXT_HPAD); - text_p.v_pad(STATUS_TEXT_VPAD); - mStatusTextBox = LLUICtrlFactory::create (text_p); - mStatusTextBox->setFollowsLeft(); - mStatusTextBox->setFollowsTop(); - addChild(mStatusTextBox); - - mViewModelItem->openItem(); - - mAreChildrenInited = true; // root folder is a special case due to not being loaded normally, assume that it's inited. -} - -// Destroys the object -LLFolderView::~LLFolderView( void ) -{ - mRenamerTopLostSignalConnection.disconnect(); - if (mRenamer) - { - // instead of using closeRenamer remove it directly, - // since it might already be hidden - LLUI::getInstance()->removePopup(mRenamer); - } - - // 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 - // about calling into the invalid scroll container. - // Same with the renamer. - mScrollContainer = NULL; - mRenameItem = NULL; - mRenamer = NULL; - mStatusTextBox = NULL; - - if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); - mPopupMenuHandle.markDead(); - - mAutoOpenItems.removeAllNodes(); - clearSelection(); - mItems.clear(); - mFolders.clear(); - - //mViewModel->setFolderView(NULL); - mViewModel = NULL; -} - -bool LLFolderView::canFocusChildren() const -{ - return false; -} - -void LLFolderView::addFolder( LLFolderViewFolder* folder) -{ - LLFolderViewFolder::addFolder(folder); -} - -void LLFolderView::closeAllFolders() -{ - // Close all the folders - setOpenArrangeRecursively(false, LLFolderViewFolder::RECURSE_DOWN); - arrangeAll(); -} - -void LLFolderView::openTopLevelFolders() -{ - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setOpen(true); - } -} - -// This view grows and shrinks to enclose all of its children items and folders. -// *width should be 0 -// conform show folder state works -S32 LLFolderView::arrange( S32* unused_width, S32* unused_height ) - { - mMinWidth = 0; - S32 target_height; - - LLFolderViewFolder::arrange(&mMinWidth, &target_height); - - LLRect scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); - reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) ); - - LLRect new_scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); - if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) - { - reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) ); - } - - // move item renamer text field to item's new position - updateRenamerPosition(); - - return ll_round(mTargetHeight); -} - -void LLFolderView::filter( LLFolderViewFilter& filter ) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - const S32 TIME_VISIBLE = 10; // in milliseconds - const S32 TIME_INVISIBLE = 1; - filter.resetTime(llclamp((mParentPanel.get()->getVisible() ? TIME_VISIBLE : TIME_INVISIBLE), 1, 100)); - - // Note: we filter the model, not the view - getViewModelItem()->filter(filter); -} - -void LLFolderView::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLRect scroll_rect; - if (mScrollContainer) - { - LLView::reshape(width, height, called_from_parent); - scroll_rect = mScrollContainer->getContentWindowRect(); - } - width = llmax(mMinWidth, scroll_rect.getWidth()); - height = llmax(ll_round(mCurHeight), scroll_rect.getHeight()); - - // Restrict width within scroll container's width - if (mUseEllipses && mScrollContainer) - { - width = scroll_rect.getWidth(); - } - LLView::reshape(width, height, called_from_parent); - mReshapeSignal(mSelectedItems, false); -} - -void LLFolderView::addToSelectionList(LLFolderViewItem* item) -{ - if (item->isSelected()) - { - removeFromSelectionList(item); - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(false); - } - item->setIsCurSelection(true); - mSelectedItems.push_back(item); -} - -void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) -{ - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(false); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();) - { - if (*item_iter == item) - { - item_iter = mSelectedItems.erase(item_iter); - } - else - { - ++item_iter; - } - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(true); - } -} - -LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) -{ - if(mSelectedItems.size()) - { - LLFolderViewItem* itemp = mSelectedItems.back(); - llassert(itemp->getIsCurSelection()); - return itemp; - } - return NULL; -} - -LLFolderView::selected_items_t& LLFolderView::getSelectedItems( void ) -{ - return mSelectedItems; -} - -// Record the selected item and pass it down the hierachy. -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; - } - - if( selection && take_keyboard_focus) - { - mParentPanel.get()->setFocus(true); - } - - // clear selection down here because change of keyboard focus can potentially - // affect selection - clearSelection(); - - if(selection) - { - addToSelectionList(selection); - } - - bool rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); - if(openitem && selection) - { - selection->getParentFolder()->requestArrange(); - } - - llassert(mSelectedItems.size() <= 1); - - return rv; -} - -bool LLFolderView::changeSelection(LLFolderViewItem* selection, bool selected) -{ - bool rv = false; - - // can't select root folder - if(!selection || selection == this) - { - return false; - } - - if (!mAllowMultiSelect) - { - clearSelection(); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - if (*item_iter == selection) - { - break; - } - } - - bool on_list = (item_iter != mSelectedItems.end()); - - if(selected && !on_list) - { - addToSelectionList(selection); - } - if(!selected && on_list) - { - removeFromSelectionList(selection); - } - - rv = LLFolderViewFolder::changeSelection(selection, selected); - - mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; - - return rv; -} - -void LLFolderView::sanitizeSelection() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - // store off current item in case it is automatically deselected - // and we want to preserve context - LLFolderViewItem* original_selected_item = getCurSelectedItem(); - - std::vector items_to_remove; - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - LLFolderViewItem* item = *item_iter; - - // ensure that each ancestor is open and potentially passes filtering - bool visible = false; - if(item->getViewModelItem() != NULL) - { - visible = item->getViewModelItem()->potentiallyVisible(); // initialize from filter state for this item - } - // modify with parent open and filters states - LLFolderViewFolder* parent_folder = item->getParentFolder(); - // Move up through parent folders and see what's visible - while(parent_folder) - { - visible = visible && parent_folder->isOpen() && parent_folder->getViewModelItem()->potentiallyVisible(); - parent_folder = parent_folder->getParentFolder(); - } - - // deselect item if any ancestor is closed or didn't pass filter requirements. - if (!visible) - { - items_to_remove.push_back(item); - } - - // disallow nested selections (i.e. folder items plus one or more ancestors) - // could check cached mum selections count and only iterate if there are any - // but that may be a premature optimization. - selected_items_t::iterator other_item_iter; - for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter) - { - LLFolderViewItem* other_item = *other_item_iter; - for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder == item) - { - // this is a descendent of the current folder, remove from list - items_to_remove.push_back(other_item); - break; - } - } - } - - // Don't allow invisible items (such as root folders) to be selected. - if (item == getRoot()) - { - items_to_remove.push_back(item); - } - } - - std::vector::iterator item_it; - for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it ) - { - changeSelection(*item_it, false); // toggle selection (also removes from list) - } - - // if nothing selected after prior constraints... - if (mSelectedItems.empty()) - { - // ...select first available parent of original selection - LLFolderViewItem* new_selection = NULL; - if (original_selected_item) - { - for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder(); - parent_folder; - parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder->getViewModelItem() && parent_folder->getViewModelItem()->potentiallyVisible()) - { - // give initial selection to first ancestor folder that potentially passes the filter - if (!new_selection) - { - new_selection = parent_folder; - } - - // if any ancestor folder of original item is closed, move the selection up - // to the highest closed - if (!parent_folder->isOpen()) - { - new_selection = parent_folder; - } - } - } - } - else - { - new_selection = NULL; - } - - if (new_selection) - { - setSelection(new_selection, false, false); - } - } -} - -void LLFolderView::clearSelection() -{ - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); - ++item_it) - { - (*item_it)->setUnselected(); - } - - mSelectedItems.clear(); - mNeedsScroll = false; -} - -std::set LLFolderView::getSelectionList() const -{ - std::set selection; - std::copy(mSelectedItems.begin(), mSelectedItems.end(), std::inserter(selection, selection.begin())); - return selection; -} - -bool LLFolderView::startDrag() -{ - std::vector selected_items; - selected_items_t::iterator item_it; - - if (!mSelectedItems.empty()) - { - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - selected_items.push_back((*item_it)->getViewModelItem()); - } - - return getFolderViewModel()->startDrag(selected_items); - } - return false; -} - -void LLFolderView::commitRename( const LLSD& data ) -{ - finishRenamingItem(); - arrange( NULL, NULL ); - -} - -void LLFolderView::draw() -{ - //LLFontGL* font = getLabelFontForStyle(mLabelStyle); - - // if cursor has moved off of me during drag and drop - // close all auto opened folders - if (!mDragAndDropThisFrame) - { - closeAutoOpenedFolders(); - } - - static LLCachedControl type_ahead_timeout(*LLUI::getInstance()->mSettingGroups["config"], "TypeAheadTimeout", 1.5f); - if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout || !mSearchString.size()) - { - mSearchString.clear(); - } - - if (hasVisibleChildren()) - { - mStatusTextBox->setVisible( false ); - } - else if (mShowEmptyMessage) - { - mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty())); - 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. - S32 height = 0; - S32 width = 0; - S32 total_height = arrange( &width, &height ); - notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); - - LLUI::popMatrix(); - LLUI::pushMatrix(); - LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); - } - } - - if (mRenameItem - && mRenamer - && mRenamer->getVisible() - && !getVisibleRect().overlaps(mRenamer->getRect())) - { - // renamer is not connected to the item we are renaming in any form so manage it manually - // TODO: consider stopping on any scroll action instead of when out of visible area - LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL; - finishRenamingItem(); - } - - // skip over LLFolderViewFolder::draw since we don't want the folder icon, label, - // and arrow for the root folder - LLView::draw(); - - mDragAndDropThisFrame = false; -} - -void LLFolderView::finishRenamingItem( void ) -{ - if(!mRenamer) - { - return; - } - if( mRenameItem ) - { - mRenameItem->rename( mRenamer->getText() ); - } - - closeRenamer(); - - // This is moved to an inventory observer in llinventorybridge.cpp, to handle updating after operation completed in AISv3 (SH-4611). - // List is re-sorted alphabetically, so scroll to make sure the selected item is visible. - //scrollToShowSelection(); -} - -void LLFolderView::closeRenamer( void ) -{ - if (mRenamer && mRenamer->getVisible()) - { - // Triggers onRenamerLost() that actually closes the renamer. - LLUI::getInstance()->removePopup(mRenamer); - } -} - -void LLFolderView::removeSelectedItems() -{ - if(getVisible() && getEnabled()) - { - // just in case we're removing the renaming item. - mRenameItem = NULL; - - // create a temporary structure which we will use to remove - // items, since the removal will futz with internal data - // structures. - std::vector items; - S32 count = mSelectedItems.size(); - if(count <= 0) return; - LLFolderViewItem* item = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - item = *item_it; - if (item && item->isRemovable()) - { - items.push_back(item); - } - else - { - LL_DEBUGS() << "Cannot delete " << item->getName() << LL_ENDL; - return; - } - } - - // iterate through the new container. - count = items.size(); - LLUUID new_selection_id; - LLFolderViewItem* item_to_select = getNextUnselectedItem(); - - if(count == 1) - { - LLFolderViewItem* item_to_delete = items[0]; - LLFolderViewFolder* parent = item_to_delete->getParentFolder(); - if(parent) - { - if (item_to_delete->remove()) - { - // change selection on successful delete - setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus()); - } - } - arrangeAll(); - } - else if (count > 1) - { - std::vector listeners; - LLFolderViewModelItem* listener; - - setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus()); - - listeners.reserve(count); - for(S32 i = 0; i < count; ++i) - { - listener = items[i]->getViewModelItem(); - if(listener && (std::find(listeners.begin(), listeners.end(), listener) == listeners.end())) - { - listeners.push_back(listener); - } - } - listener = static_cast(listeners.at(0)); - if(listener) - { - listener->removeBatch(listeners); - } - } - arrangeAll(); - scrollToShowSelection(); - } -} - -void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) -{ - if ((mAutoOpenItems.check() == item) || - (mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) || - item->isOpen()) - { - return; - } - - // close auto-opened folders - LLFolderViewFolder* close_item = mAutoOpenItems.check(); - while (close_item && close_item != item->getParentFolder()) - { - mAutoOpenItems.pop(); - close_item->setOpenArrangeRecursively(false); - close_item = mAutoOpenItems.check(); - } - - item->requestArrange(); - - mAutoOpenItems.push(item); - - item->setOpen(true); - if(!item->isSingleFolderMode()) - { - LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); - LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); - scrollToShowItem(item, constraint_rect); - } -} - -void LLFolderView::closeAutoOpenedFolders() -{ - while (mAutoOpenItems.check()) - { - LLFolderViewFolder* close_item = mAutoOpenItems.pop(); - close_item->setOpen(false); - } - - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); -} - -bool LLFolderView::autoOpenTest(LLFolderViewFolder* folder) -{ - if (folder && mAutoOpenCandidate == folder) - { - if (mAutoOpenTimer.getStarted()) - { - if (!mAutoOpenCandidate->isOpen()) - { - mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f)); - } - if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime) - { - autoOpenItem(folder); - mAutoOpenTimer.stop(); - return true; - } - } - return false; - } - - // otherwise new candidate, restart timer - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = folder; - mAutoOpenTimer.start(); - return false; -} - -bool LLFolderView::canCopy() const -{ - 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; - if (!item->getViewModelItem()->isItemCopyable()) - { - return false; - } - } - return true; -} - -// copy selected item -void LLFolderView::copy() -{ - // *NOTE: total hack to clear the inventory clipboard - LLClipboard::instance().reset(); - S32 count = mSelectedItems.size(); - if(getVisible() && getEnabled() && (count > 0)) - { - LLFolderViewModelItem* listener = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - listener = (*item_it)->getViewModelItem(); - if(listener) - { - listener->copyToClipboard(); - } - } - } - mSearchString.clear(); -} - -bool LLFolderView::canCut() const -{ - 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 LLFolderViewModelItem* listener = item->getViewModelItem(); - - if (!listener || !listener->isItemRemovable()) - { - return false; - } - } - return true; -} - -void LLFolderView::cut() -{ - // clear the inventory clipboard - LLClipboard::instance().reset(); - if(getVisible() && getEnabled() && (mSelectedItems.size() > 0)) - { - // Find out which item will be selected once the selection will be cut - LLFolderViewItem* item_to_select = getNextUnselectedItem(); - - // Get the selection: removeItem() modified mSelectedItems and makes iterating on it unwise - std::set inventory_selected = getSelectionList(); - - // Move each item to the clipboard and out of their folder - for (std::set::iterator item_it = inventory_selected.begin(); item_it != inventory_selected.end(); ++item_it) - { - LLFolderViewItem* item_to_cut = *item_it; - LLFolderViewModelItem* listener = item_to_cut->getViewModelItem(); - if (listener) - { - listener->cutToClipboard(); - } - } - - // Update the selection - setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus()); - } - mSearchString.clear(); -} - -bool LLFolderView::canPaste() const -{ - if (mSelectedItems.empty()) - { - return false; - } - - if(getVisible() && getEnabled()) - { - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); ++item_it) - { - // *TODO: only check folders and parent folders of items - const LLFolderViewItem* item = (*item_it); - const LLFolderViewModelItem* listener = item->getViewModelItem(); - if(!listener || !listener->isClipboardPasteable()) - { - const LLFolderViewFolder* folderp = item->getParentFolder(); - listener = folderp->getViewModelItem(); - if (!listener || !listener->isClipboardPasteable()) - { - return false; - } - } - } - return true; - } - return false; -} - -// paste selected item -void LLFolderView::paste() -{ - if(getVisible() && getEnabled()) - { - // find set of unique folders to paste into - std::set folder_set; - - selected_items_t::iterator selected_it; - for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - LLFolderViewItem* item = *selected_it; - LLFolderViewFolder* folder = dynamic_cast(item); - if (folder == NULL) - { - folder = item->getParentFolder(); - } - folder_set.insert(folder); - } - - std::set::iterator set_iter; - for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) - { - LLFolderViewModelItem* listener = (*set_iter)->getViewModelItem(); - if(listener && listener->isClipboardPasteable()) - { - listener->pasteFromClipboard(); - } - } - } - mSearchString.clear(); -} - -// public rename functionality - can only start the process -void LLFolderView::startRenamingSelectedItem( void ) -{ - LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL; - - // make sure selection is visible - scrollToShowSelection(); - - S32 count = mSelectedItems.size(); - LLFolderViewItem* item = NULL; - if(count > 0) - { - item = mSelectedItems.front(); - } - if(getVisible() && getEnabled() && (count == 1) && item && item->getViewModelItem() && - item->getViewModelItem()->isItemRenameable()) - { - mRenameItem = item; - - updateRenamerPosition(); - - - mRenamer->setText(item->getName()); - mRenamer->selectAll(); - mRenamer->setVisible( true ); - // set focus will fail unless item is visible - mRenamer->setFocus( true ); - if (!mRenamerTopLostSignalConnection.connected()) - { - mRenamerTopLostSignalConnection = mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); - } - LLUI::getInstance()->addPopup(mRenamer); - } -} - -bool LLFolderView::handleKeyHere( KEY key, MASK mask ) -{ - bool handled = false; - - // 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(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - switch( key ) - { - case KEY_F2: - mSearchString.clear(); - startRenamingSelectedItem(); - handled = true; - break; - - case KEY_RETURN: - if (mask == MASK_NONE) - { - if( mRenameItem && mRenamer->getVisible() ) - { - finishRenamingItem(); - mSearchString.clear(); - handled = true; - } - } - break; - - case KEY_ESCAPE: - if( mRenameItem && mRenamer->getVisible() ) - { - closeRenamer(); - handled = true; - } - mSearchString.clear(); - break; - - case KEY_PAGE_UP: - mSearchString.clear(); - if (mScrollContainer) - { - mScrollContainer->pageUp(30); - } - handled = true; - break; - - case KEY_PAGE_DOWN: - mSearchString.clear(); - if (mScrollContainer) - { - mScrollContainer->pageDown(30); - } - handled = true; - break; - - case KEY_HOME: - mSearchString.clear(); - if (mScrollContainer) - { - mScrollContainer->goToTop(); - } - handled = true; - break; - - case KEY_END: - mSearchString.clear(); - if (mScrollContainer) - { - mScrollContainer->goToBottom(); - } - break; - - case KEY_DOWN: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - bool shift_select = mask & MASK_SHIFT; - // don't shift select down to children of folders (they are implicitly selected through parent) - LLFolderViewItem* next = last_selected->getNextOpenNode(!shift_select); - - if (!mKeyboardSelection || (!shift_select && (!next || next == last_selected))) - { - setSelection(last_selected, false, true); - mKeyboardSelection = true; - } - - if (shift_select) - { - if (next) - { - if (next->isSelected()) - { - // shrink selection - changeSelection(last_selected, false); - } - else if (last_selected->getParentFolder() == next->getParentFolder()) - { - // grow selection - changeSelection(next, true); - } - } - } - else - { - if( next ) - { - 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(); - handled = true; - } - break; - - case KEY_UP: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = mSelectedItems.back(); - bool shift_select = mask & MASK_SHIFT; - // don't shift select down to children of folders (they are implicitly selected through parent) - LLFolderViewItem* prev = last_selected->getPreviousOpenNode(!shift_select); - - if (!mKeyboardSelection || (!shift_select && prev == this)) - { - setSelection(last_selected, false, true); - mKeyboardSelection = true; - } - - if (shift_select) - { - if (prev) - { - if (prev->isSelected()) - { - // shrink selection - changeSelection(last_selected, false); - } - else if (last_selected->getParentFolder() == prev->getParentFolder()) - { - // grow selection - changeSelection(prev, true); - } - } - } - else - { - if( prev ) - { - 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 ); - } - } - scrollToShowSelection(); - mSearchString.clear(); - - handled = true; - } - break; - - case KEY_RIGHT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - last_selected->setOpen( true ); - mSearchString.clear(); - handled = true; - } - break; - - case KEY_LEFT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - if(last_selected && last_selected->isSingleFolderMode()) - { - handled = false; - break; - } - LLFolderViewItem* parent_folder = last_selected->getParentFolder(); - if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) - { - setSelection(parent_folder, false, true); - } - else - { - last_selected->setOpen( false ); - } - mSearchString.clear(); - scrollToShowSelection(); - handled = true; - } - break; - } - - return handled; -} - - -bool LLFolderView::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return false; - } - - if (uni_char > 0x7f) - { - LL_WARNS() << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << LL_ENDL; - return false; - } - - bool handled = false; - if (mParentPanel.get()->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. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - //do text search - if (mSearchTimer.getElapsedTimeF32() > LLUI::getInstance()->mSettingGroups["config"]->getF32("TypeAheadTimeout")) - { - mSearchString.clear(); - } - mSearchTimer.reset(); - if (mSearchString.size() < 128) - { - mSearchString += uni_char; - } - search(getCurSelectedItem(), mSearchString, false); - - handled = true; - } - - return handled; -} - - -bool LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - mKeyboardSelection = false; - mSearchString.clear(); - - mParentPanel.get()->setFocus(true); - - LLEditMenuHandler::gEditMenuHandler = this; - - return LLView::handleMouseDown( x, y, mask ); -} - -bool LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, bool backward) -{ - // get first selected item - LLFolderViewItem* search_item = first_item; - - // make sure search string is upper case - std::string upper_case_string = search_string; - LLStringUtil::toUpper(upper_case_string); - - // if nothing selected, select first item in folder - if (!search_item) - { - // start from first item - search_item = getNextFromChild(NULL); - } - - // search over all open nodes for first substring match (with wrapping) - bool found = false; - LLFolderViewItem* original_search_item = search_item; - do - { - // wrap at end - if (!search_item) - { - if (backward) - { - search_item = getPreviousFromChild(NULL); - } - else - { - search_item = getNextFromChild(NULL); - } - if (!search_item || search_item == original_search_item) - { - break; - } - } - - std::string current_item_label(search_item->getViewModelItem()->getSearchableName()); - LLStringUtil::toUpper(current_item_label); - S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); - if (!current_item_label.compare(0, search_string_length, upper_case_string)) - { - found = true; - break; - } - if (backward) - { - search_item = search_item->getPreviousOpenNode(); - } - else - { - search_item = search_item->getNextOpenNode(); - } - - } while(search_item != original_search_item); - - - if (found) - { - setSelection(search_item, false, true); - scrollToShowSelection(); - } - - return found; -} - -bool LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - // skip LLFolderViewFolder::handleDoubleClick() - return LLView::handleDoubleClick( x, y, mask ); -} - -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 - mParentPanel.get()->setFocus(true); - - bool handled = childrenHandleRightMouseDown(x, y, mask) != NULL; - S32 count = mSelectedItems.size(); - - LLMenuGL* menu = static_cast(mPopupMenuHandle.get()); - if (!menu) - { - if (mCallbackRegistrar) - { - mCallbackRegistrar->pushScope(); - } - if (mEnableRegistrar) - { - mEnableRegistrar->pushScope(); - } - llassert(LLMenuGL::sMenuContainer != NULL); - menu = LLUICtrlFactory::getInstance()->createFromFile(mMenuFileName, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); - if (!menu) - { - menu = LLUICtrlFactory::getDefaultWidget("inventory_menu"); - } - menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); - mPopupMenuHandle = menu->getHandle(); - if (mEnableRegistrar) - { - mEnableRegistrar->popScope(); - } - if (mCallbackRegistrar) - { - mCallbackRegistrar->popScope(); - } - } - - bool item_clicked{ false }; - for (const auto item : mSelectedItems) - { - item_clicked |= item->getRect().pointInRect(x, y); - } - if(!item_clicked && mSingleFolderMode) - { - clearSelection(); - } - bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected(); - if (menu && (mSingleFolderMode || (handled - && ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible - !hide_folder_menu) - { - if (mCallbackRegistrar) - { - mCallbackRegistrar->pushScope(); - } - if (mEnableRegistrar) - { - mEnableRegistrar->pushScope(); - } - - updateMenuOptions(menu); - - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, x, y); - if (mEnableRegistrar) - { - mEnableRegistrar->popScope(); - } - if (mCallbackRegistrar) - { - mCallbackRegistrar->popScope(); - } - } - else - { - if (menu && menu->getVisible()) - { - menu->setVisible(false); - } - setSelection(NULL, false, true); - } - 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 ); -} - -LLFolderViewItem* LLFolderView::getHoveredItem() const -{ - return dynamic_cast(mHoveredItem.get()); -} - -void LLFolderView::setHoveredItem(LLFolderViewItem* itemp) -{ - if (mHoveredItem.get() != itemp) - { - if (itemp) - mHoveredItem = itemp->getHandle(); - else - mHoveredItem.markDead(); - } -} - -bool LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - 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) - { - handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - } - - return handled; -} - -void LLFolderView::deleteAllChildren() -{ - mRenamerTopLostSignalConnection.disconnect(); - if (mRenamer) - { - LLUI::getInstance()->removePopup(mRenamer); - } - if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); - mPopupMenuHandle.markDead(); - mScrollContainer = NULL; - mRenameItem = NULL; - mRenamer = NULL; - mStatusTextBox = NULL; - - clearSelection(); - LLView::deleteAllChildren(); -} - -void LLFolderView::scrollToShowSelection() -{ - if ( mSelectedItems.size() ) - { - mNeedsScroll = true; - } -} - -// If the parent is scroll container, scroll it to make the selection -// is maximally visible. -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 exists and is in visible portion of parent folder... - if(item) - { - LLRect local_rect = item->getLocalRect(); - S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); - S32 label_height = 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 ) + item->getIconPad()) : local_rect.getHeight(); - - // get portion of item that we want to see... - LLRect item_local_rect = LLRect(item->getIndentation(), - local_rect.getHeight(), - //+40 is supposed to include few first characters - llmin(item->getLabelXPos() - item->getIndentation() + 40, local_rect.getWidth()), - llmax(0, local_rect.getHeight() - max_height_to_show)); - - LLRect item_doc_rect; - - item->localRectToOtherView(item_local_rect, &item_doc_rect, this); - - mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect ); - - } -} - -LLRect LLFolderView::getVisibleRect() -{ - S32 visible_height = (mScrollContainer ? mScrollContainer->getRect().getHeight() : 0); - S32 visible_width = (mScrollContainer ? mScrollContainer->getRect().getWidth() : 0); - LLRect visible_rect; - visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); - return visible_rect; -} - -bool LLFolderView::getShowSelectionContext() -{ - if (mShowSelectionContext) - { - return true; - } - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->getVisible()) - { - return true; - } - return false; -} - -void LLFolderView::setShowSingleSelection(bool show) -{ - if (show != mShowSingleSelection) - { - mMultiSelectionFadeTimer.reset(); - mShowSingleSelection = show; - } -} - -static LLTrace::BlockTimerStatHandle FTM_INVENTORY("Inventory"); - -// Main idle routine -void LLFolderView::update() -{ - // If this is associated with the user's inventory, don't do anything - // until that inventory is loaded up. - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; //LL_RECORD_BLOCK_TIME(FTM_INVENTORY); - - // If there's no model, the view is in suspended state (being deleted) and shouldn't be updated - if (getFolderViewModel() == NULL) - { - return; - } - - LLFolderViewFilter& filter_object = getFolderViewModel()->getFilter(); - - if (filter_object.isModified() && filter_object.isNotDefault() && mParentPanel.get()->getVisible()) - { - mNeedsAutoSelect = true; - } - - // Filter to determine visibility before arranging - filter(filter_object); - - // Clear the modified setting on the filter only if the filter finished after running the filter process - // Note: if the filter count has timed out, that means the filter halted before completing the entire set of items - bool filter_modified = filter_object.isModified(); - if (filter_modified && (!filter_object.isTimedOut())) - { - filter_object.clearModified(); - } - - // automatically show matching items, and select first one if we had a selection - if (mNeedsAutoSelect) - { - // select new item only if a filtered item not currently selected and there was a selection - LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); - if (!mAutoSelectOverride && selected_itemp && !selected_itemp->getViewModelItem()->potentiallyVisible()) - { - // these are named variables to get around gcc not binding non-const references to rvalues - // and functor application is inherently non-const to allow for stateful functors - LLSelectFirstFilteredItem functor; - applyFunctorRecursively(functor); - } - - // Open filtered folders for folder views with mAutoSelectOverride=true. - // Used by LLPlacesFolderView. - if (filter_object.showAllResults()) - { - // these are named variables to get around gcc not binding non-const references to rvalues - // and functor application is inherently non-const to allow for stateful functors - LLOpenFilteredFolders functor; - applyFunctorRecursively(functor); - } - - scrollToShowSelection(); - } - - bool filter_finished = mViewModel->contentsReady() - && (getViewModelItem()->passedFilter() - || ( getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration() - && !filter_modified)); - if (filter_finished - || gFocusMgr.childHasKeyboardFocus(mParentPanel.get()) - || gFocusMgr.childHasMouseCapture(mParentPanel.get())) - { - // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process - mNeedsAutoSelect = false; - } - - bool is_visible = isInVisibleChain() || mForceArrange; - - //Puts folders/items in proper positions - // arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849) - // It also handles the open/close folder animation - if ( is_visible ) - { - sanitizeSelection(); - if( needsArrange() ) - { - S32 height = 0; - S32 width = 0; - S32 total_height = arrange( &width, &height ); - notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); - } - } - - // 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_finished) - { - // calculate rectangle to pin item to at start of animated rearrange - if (!mPinningSelectedItem && !mSelectedItems.empty()) - { - // lets pin it! - mPinningSelectedItem = true; - - //Computes visible area - const LLRect visible_content_rect = (mScrollContainer ? mScrollContainer->getVisibleContentRect() : LLRect()); - LLFolderViewItem* selected_item = mSelectedItems.back(); - - //Computes location of selected content, content outside visible area will be scrolled to using below code - LLRect item_rect; - selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this); - - //Computes intersected region of the selected content and visible area - LLRect overlap_rect(item_rect); - overlap_rect.intersectWith(visible_content_rect); - - //Don't scroll when the selected content exists within the visible area - if (overlap_rect.getHeight() >= selected_item->getItemHeight()) - { - // then attempt to keep it in same place on screen - mScrollConstraintRect = item_rect; - mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom); - } - //Scroll because the selected content is outside the visible area - else - { - // otherwise we just want it onscreen somewhere - LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); - 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 ? mScrollContainer->getContentWindowRect() : LLRect()); - constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); - } - - if (mSelectedItems.size() && mNeedsScroll) - { - LLFolderViewItem* scroll_to_item = mSelectedItems.back(); - scrollToShowItem(scroll_to_item, constraint_rect); - // continue scrolling until animated layout change is done - bool selected_filter_finished = getRoot()->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration(); - if (selected_filter_finished && scroll_to_item && scroll_to_item->getViewModelItem()) - { - selected_filter_finished = scroll_to_item->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration(); - } - if (filter_finished && selected_filter_finished) - { - bool needs_arrange = needsArrange() || getRoot()->needsArrange(); - if (mParentFolder) - { - needs_arrange |= (bool)mParentFolder->needsArrange(); - } - if (!needs_arrange || !is_visible) - { - mNeedsScroll = false; - } - } - } - - if (mSelectedItems.size()) - { - LLFolderViewItem* item = mSelectedItems.back(); - // If the goal is to show renamer, don't callback untill - // item is visible or is no longer being scrolled to. - // Otherwise renamer will be instantly closed - // Todo: consider moving renamer out of selection callback - if (!mNeedsAutoRename || !mNeedsScroll || item->getVisible()) - { - if (mSignalSelectCallback) - { - //RN: we use keyboard focus as a proxy for user-explicit actions - bool take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); - mSelectSignal(mSelectedItems, take_keyboard_focus); - } - mSignalSelectCallback = false; - } - } - else - { - mSignalSelectCallback = false; - } -} - -void LLFolderView::dumpSelectionInformation() -{ - LL_INFOS() << "LLFolderView::dumpSelectionInformation()" << LL_NEWLINE - << "****************************************" << LL_ENDL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - LL_INFOS() << " " << (*item_it)->getName() << LL_ENDL; - } - LL_INFOS() << "****************************************" << LL_ENDL; -} - -void LLFolderView::updateRenamerPosition() -{ - if(mRenameItem) - { - // See also LLFolderViewItem::draw() - S32 x = mRenameItem->getLabelXPos(); - 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, (S32)LLUI::getInstance()->getWindowSize().mV[VX], 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 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0); - U32 flags = multi_select_flag | 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 = multi_select_flag; - } - - if(mSingleFolderMode && (mSelectedItems.size() == 0)) - { - buildContextMenu(*menu, flags); - } - - // This adds a check for restrictions based on the entire - // selection set - for example, any one wearable may not push you - // over the limit, but all wearables together still might. - if (getFolderViewGroupedItemModel()) - { - getFolderViewGroupedItemModel()->groupFilterContextMenu(mSelectedItems,*menu); - } - - 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::isFolderSelected() -{ - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - LLFolderViewFolder* folder = dynamic_cast(*item_iter); - if (folder != NULL) - { - return true; - } - } - return false; -} - -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 -///---------------------------------------------------------------------------- - -void LLFolderView::onRenamerLost() -{ - if (mRenamer && mRenamer->getVisible()) - { - mRenamer->setVisible(false); - - // will commit current name (which could be same as original name) - mRenamer->setFocus(false); - } - - if( mRenameItem ) - { - setSelection( mRenameItem, true ); - mRenameItem = NULL; - } -} - -LLFolderViewItem* LLFolderView::getNextUnselectedItem() -{ - LLFolderViewItem* last_item = *mSelectedItems.rbegin(); - LLFolderViewItem* new_selection = last_item->getNextOpenNode(false); - while(new_selection && new_selection->isSelected()) - { - new_selection = new_selection->getNextOpenNode(false); - } - if (!new_selection) - { - new_selection = last_item->getPreviousOpenNode(false); - while (new_selection && (new_selection->isInSelection())) - { - new_selection = new_selection->getPreviousOpenNode(false); - } - } - return new_selection; -} - -S32 LLFolderView::getItemHeight() const -{ - if(!hasVisibleChildren()) -{ - //We need to display status textbox, let's reserve some place for it - return llmax(0, mStatusTextBox->getTextPixelHeight()); -} - return 0; -} +/** + * @file llfolderview.cpp + * @brief Implementation of the folder view collection of classes. + * + * $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 "linden_common.h" + +#include "llfolderview.h" +#include "llfolderviewmodel.h" +#include "llclipboard.h" // *TODO: remove this once hack below gone. +#include "llkeyboard.h" +#include "lllineeditor.h" +#include "llmenugl.h" +#include "llpanel.h" +#include "llscrollcontainer.h" // hack to allow scrolling +#include "lltextbox.h" +#include "lltrans.h" +#include "llui.h" +#include "lluictrlfactory.h" + +// Linden library includes +#include "lldbstrings.h" +#include "llfocusmgr.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llrender.h" + +// Third-party library includes +#include + +///---------------------------------------------------------------------------- +/// Local function declarations, constants, enums, and typedefs +///---------------------------------------------------------------------------- + +const S32 RENAME_HEIGHT_PAD = 1; +const S32 AUTO_OPEN_STACK_DEPTH = 16; + +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, + SIGNAL_KEYBOARD_FOCUS = 2 +}; + +F32 LLFolderView::sAutoOpenTime = 1.f; + +//--------------------------------------------------------------------------- + +// Tells all folders in a folderview to close themselves +// For efficiency, calls setOpenArrangeRecursively(). +// The calling function must then call: +// LLFolderView* root = getRoot(); +// if( root ) +// { +// root->arrange( NULL, NULL ); +// root->scrollToShowSelection(); +// } +// to patch things up. +class LLCloseAllFoldersFunctor : public LLFolderViewFunctor +{ +public: + LLCloseAllFoldersFunctor(bool close) { mOpen = !close; } + virtual ~LLCloseAllFoldersFunctor() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); + + bool mOpen; +}; + + +void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) +{ + folder->setOpenArrangeRecursively(mOpen); +} + +// Do nothing. +void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) +{ } + +//--------------------------------------------------------------------------- + +void LLAllDescendentsPassedFilter::doFolder(LLFolderViewFolder* folder) +{ + mAllDescendentsPassedFilter &= (folder) && (folder->passedFilter()) && (folder->descendantsPassedFilter()); +} + +void LLAllDescendentsPassedFilter::doItem(LLFolderViewItem* item) +{ + mAllDescendentsPassedFilter &= (item) && (item->passedFilter()); +} + +///---------------------------------------------------------------------------- +/// Class LLFolderViewScrollContainer +///---------------------------------------------------------------------------- + +// virtual +const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const +{ + LLRect rect = LLRect::null; + if (mScrolledView) + { + LLFolderView* folder_view = dynamic_cast(mScrolledView); + if (folder_view) + { + S32 height = folder_view->getRect().getHeight(); + + rect = mScrolledView->getRect(); + rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height); + } + } + + return rect; +} + +LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer::Params& p) +: LLScrollContainer(p) +{} + +///---------------------------------------------------------------------------- +/// Class LLFolderView +///---------------------------------------------------------------------------- +LLFolderView::Params::Params() +: title("title"), + use_label_suffix("use_label_suffix"), + allow_multiselect("allow_multiselect", true), + allow_drag("allow_drag", true), + show_empty_message("show_empty_message", true), + suppress_folder_menu("suppress_folder_menu", false), + use_ellipses("use_ellipses", false), + options_menu("options_menu", "") +{ + folder_indentation = -4; +} + + +// Default constructor +LLFolderView::LLFolderView(const Params& p) +: LLFolderViewFolder(p), + mScrollContainer( NULL ), + mPopupMenuHandle(), + mMenuFileName(p.options_menu), + mAllowMultiSelect(p.allow_multiselect), + mAllowDrag(p.allow_drag), + mShowEmptyMessage(p.show_empty_message), + mShowFolderHierarchy(false), + mRenameItem( NULL ), + mNeedsScroll( false ), + mUseLabelSuffix(p.use_label_suffix), + mSuppressFolderMenu(p.suppress_folder_menu), + mPinningSelectedItem(false), + mNeedsAutoSelect( false ), + mAutoSelectOverride(false), + mNeedsAutoRename(false), + mShowSelectionContext(false), + mShowSingleSelection(false), + mArrangeGeneration(0), + mSignalSelectCallback(0), + mMinWidth(0), + mDragAndDropThisFrame(false), + mCallbackRegistrar(NULL), + mEnableRegistrar(NULL), + mUseEllipses(p.use_ellipses), + mDraggingOverItem(NULL), + mStatusTextBox(NULL), + mShowItemLinkOverlays(p.show_item_link_overlays), + mViewModel(p.view_model), + mGroupedItemModel(p.grouped_item_model), + mForceArrange(false), + mSingleFolderMode(false) +{ + LLPanel* panel = p.parent_panel; + mParentPanel = panel->getHandle(); + mViewModel->setFolderView(this); + mRoot = this; + + LLRect rect = p.rect; + LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); + setRect( rect ); + reshape(rect.getWidth(), rect.getHeight()); + mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); + mAutoOpenCandidate = NULL; + mAutoOpenTimer.stop(); + mKeyboardSelection = false; + mIndentation = getParentFolder() ? getParentFolder()->getIndentation() + mLocalIndentation : 0; + + //clear label + // go ahead and render root folder as usual + // just make sure the label ("Inventory Folder") never shows up + mLabel = LLStringUtil::null; + + // Escape is handled by reverting the rename, not commiting it (default behavior) + LLLineEditor::Params params; + params.name("ren"); + params.rect(rect); + params.font(getLabelFontForStyle(LLFontGL::NORMAL)); + params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN); + params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2)); + params.prevalidator(&LLTextValidate::validateASCIIPrintableNoPipe); + params.commit_on_focus_lost(true); + params.visible(false); + mRenamer = LLUICtrlFactory::create (params); + addChild(mRenamer); + + // Textbox + LLTextBox::Params text_p; + LLFontGL* font = getLabelFontForStyle(mLabelStyle); + //mIconPad, mTextPad are set in folder_view_item.xml + LLRect new_r = LLRect(rect.mLeft + mIconPad, + rect.mTop - mTextPad, + rect.mRight, + rect.mTop - mTextPad - font->getLineHeight()); + text_p.rect(new_r); + text_p.name(std::string(p.name)); + text_p.font(font); + text_p.visible(false); + text_p.parse_urls(true); + text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047 + // set text padding the same as in People panel. EXT-7047, EXT-4837 + text_p.h_pad(STATUS_TEXT_HPAD); + text_p.v_pad(STATUS_TEXT_VPAD); + mStatusTextBox = LLUICtrlFactory::create (text_p); + mStatusTextBox->setFollowsLeft(); + mStatusTextBox->setFollowsTop(); + addChild(mStatusTextBox); + + mViewModelItem->openItem(); + + mAreChildrenInited = true; // root folder is a special case due to not being loaded normally, assume that it's inited. +} + +// Destroys the object +LLFolderView::~LLFolderView( void ) +{ + mRenamerTopLostSignalConnection.disconnect(); + if (mRenamer) + { + // instead of using closeRenamer remove it directly, + // since it might already be hidden + LLUI::getInstance()->removePopup(mRenamer); + } + + // 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 + // about calling into the invalid scroll container. + // Same with the renamer. + mScrollContainer = NULL; + mRenameItem = NULL; + mRenamer = NULL; + mStatusTextBox = NULL; + + if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); + mPopupMenuHandle.markDead(); + + mAutoOpenItems.removeAllNodes(); + clearSelection(); + mItems.clear(); + mFolders.clear(); + + //mViewModel->setFolderView(NULL); + mViewModel = NULL; +} + +bool LLFolderView::canFocusChildren() const +{ + return false; +} + +void LLFolderView::addFolder( LLFolderViewFolder* folder) +{ + LLFolderViewFolder::addFolder(folder); +} + +void LLFolderView::closeAllFolders() +{ + // Close all the folders + setOpenArrangeRecursively(false, LLFolderViewFolder::RECURSE_DOWN); + arrangeAll(); +} + +void LLFolderView::openTopLevelFolders() +{ + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + (*fit)->setOpen(true); + } +} + +// This view grows and shrinks to enclose all of its children items and folders. +// *width should be 0 +// conform show folder state works +S32 LLFolderView::arrange( S32* unused_width, S32* unused_height ) + { + mMinWidth = 0; + S32 target_height; + + LLFolderViewFolder::arrange(&mMinWidth, &target_height); + + LLRect scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); + reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) ); + + LLRect new_scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); + if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) + { + reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) ); + } + + // move item renamer text field to item's new position + updateRenamerPosition(); + + return ll_round(mTargetHeight); +} + +void LLFolderView::filter( LLFolderViewFilter& filter ) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + const S32 TIME_VISIBLE = 10; // in milliseconds + const S32 TIME_INVISIBLE = 1; + filter.resetTime(llclamp((mParentPanel.get()->getVisible() ? TIME_VISIBLE : TIME_INVISIBLE), 1, 100)); + + // Note: we filter the model, not the view + getViewModelItem()->filter(filter); +} + +void LLFolderView::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLRect scroll_rect; + if (mScrollContainer) + { + LLView::reshape(width, height, called_from_parent); + scroll_rect = mScrollContainer->getContentWindowRect(); + } + width = llmax(mMinWidth, scroll_rect.getWidth()); + height = llmax(ll_round(mCurHeight), scroll_rect.getHeight()); + + // Restrict width within scroll container's width + if (mUseEllipses && mScrollContainer) + { + width = scroll_rect.getWidth(); + } + LLView::reshape(width, height, called_from_parent); + mReshapeSignal(mSelectedItems, false); +} + +void LLFolderView::addToSelectionList(LLFolderViewItem* item) +{ + if (item->isSelected()) + { + removeFromSelectionList(item); + } + if (mSelectedItems.size()) + { + mSelectedItems.back()->setIsCurSelection(false); + } + item->setIsCurSelection(true); + mSelectedItems.push_back(item); +} + +void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) +{ + if (mSelectedItems.size()) + { + mSelectedItems.back()->setIsCurSelection(false); + } + + selected_items_t::iterator item_iter; + for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();) + { + if (*item_iter == item) + { + item_iter = mSelectedItems.erase(item_iter); + } + else + { + ++item_iter; + } + } + if (mSelectedItems.size()) + { + mSelectedItems.back()->setIsCurSelection(true); + } +} + +LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) +{ + if(mSelectedItems.size()) + { + LLFolderViewItem* itemp = mSelectedItems.back(); + llassert(itemp->getIsCurSelection()); + return itemp; + } + return NULL; +} + +LLFolderView::selected_items_t& LLFolderView::getSelectedItems( void ) +{ + return mSelectedItems; +} + +// Record the selected item and pass it down the hierachy. +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; + } + + if( selection && take_keyboard_focus) + { + mParentPanel.get()->setFocus(true); + } + + // clear selection down here because change of keyboard focus can potentially + // affect selection + clearSelection(); + + if(selection) + { + addToSelectionList(selection); + } + + bool rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); + if(openitem && selection) + { + selection->getParentFolder()->requestArrange(); + } + + llassert(mSelectedItems.size() <= 1); + + return rv; +} + +bool LLFolderView::changeSelection(LLFolderViewItem* selection, bool selected) +{ + bool rv = false; + + // can't select root folder + if(!selection || selection == this) + { + return false; + } + + if (!mAllowMultiSelect) + { + clearSelection(); + } + + selected_items_t::iterator item_iter; + for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) + { + if (*item_iter == selection) + { + break; + } + } + + bool on_list = (item_iter != mSelectedItems.end()); + + if(selected && !on_list) + { + addToSelectionList(selection); + } + if(!selected && on_list) + { + removeFromSelectionList(selection); + } + + rv = LLFolderViewFolder::changeSelection(selection, selected); + + mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; + + return rv; +} + +void LLFolderView::sanitizeSelection() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + // store off current item in case it is automatically deselected + // and we want to preserve context + LLFolderViewItem* original_selected_item = getCurSelectedItem(); + + std::vector items_to_remove; + selected_items_t::iterator item_iter; + for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) + { + LLFolderViewItem* item = *item_iter; + + // ensure that each ancestor is open and potentially passes filtering + bool visible = false; + if(item->getViewModelItem() != NULL) + { + visible = item->getViewModelItem()->potentiallyVisible(); // initialize from filter state for this item + } + // modify with parent open and filters states + LLFolderViewFolder* parent_folder = item->getParentFolder(); + // Move up through parent folders and see what's visible + while(parent_folder) + { + visible = visible && parent_folder->isOpen() && parent_folder->getViewModelItem()->potentiallyVisible(); + parent_folder = parent_folder->getParentFolder(); + } + + // deselect item if any ancestor is closed or didn't pass filter requirements. + if (!visible) + { + items_to_remove.push_back(item); + } + + // disallow nested selections (i.e. folder items plus one or more ancestors) + // could check cached mum selections count and only iterate if there are any + // but that may be a premature optimization. + selected_items_t::iterator other_item_iter; + for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter) + { + LLFolderViewItem* other_item = *other_item_iter; + for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder()) + { + if (parent_folder == item) + { + // this is a descendent of the current folder, remove from list + items_to_remove.push_back(other_item); + break; + } + } + } + + // Don't allow invisible items (such as root folders) to be selected. + if (item == getRoot()) + { + items_to_remove.push_back(item); + } + } + + std::vector::iterator item_it; + for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it ) + { + changeSelection(*item_it, false); // toggle selection (also removes from list) + } + + // if nothing selected after prior constraints... + if (mSelectedItems.empty()) + { + // ...select first available parent of original selection + LLFolderViewItem* new_selection = NULL; + if (original_selected_item) + { + for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder(); + parent_folder; + parent_folder = parent_folder->getParentFolder()) + { + if (parent_folder->getViewModelItem() && parent_folder->getViewModelItem()->potentiallyVisible()) + { + // give initial selection to first ancestor folder that potentially passes the filter + if (!new_selection) + { + new_selection = parent_folder; + } + + // if any ancestor folder of original item is closed, move the selection up + // to the highest closed + if (!parent_folder->isOpen()) + { + new_selection = parent_folder; + } + } + } + } + else + { + new_selection = NULL; + } + + if (new_selection) + { + setSelection(new_selection, false, false); + } + } +} + +void LLFolderView::clearSelection() +{ + for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); + item_it != mSelectedItems.end(); + ++item_it) + { + (*item_it)->setUnselected(); + } + + mSelectedItems.clear(); + mNeedsScroll = false; +} + +std::set LLFolderView::getSelectionList() const +{ + std::set selection; + std::copy(mSelectedItems.begin(), mSelectedItems.end(), std::inserter(selection, selection.begin())); + return selection; +} + +bool LLFolderView::startDrag() +{ + std::vector selected_items; + selected_items_t::iterator item_it; + + if (!mSelectedItems.empty()) + { + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + selected_items.push_back((*item_it)->getViewModelItem()); + } + + return getFolderViewModel()->startDrag(selected_items); + } + return false; +} + +void LLFolderView::commitRename( const LLSD& data ) +{ + finishRenamingItem(); + arrange( NULL, NULL ); + +} + +void LLFolderView::draw() +{ + //LLFontGL* font = getLabelFontForStyle(mLabelStyle); + + // if cursor has moved off of me during drag and drop + // close all auto opened folders + if (!mDragAndDropThisFrame) + { + closeAutoOpenedFolders(); + } + + static LLCachedControl type_ahead_timeout(*LLUI::getInstance()->mSettingGroups["config"], "TypeAheadTimeout", 1.5f); + if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout || !mSearchString.size()) + { + mSearchString.clear(); + } + + if (hasVisibleChildren()) + { + mStatusTextBox->setVisible( false ); + } + else if (mShowEmptyMessage) + { + mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty())); + 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. + S32 height = 0; + S32 width = 0; + S32 total_height = arrange( &width, &height ); + notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); + + LLUI::popMatrix(); + LLUI::pushMatrix(); + LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); + } + } + + if (mRenameItem + && mRenamer + && mRenamer->getVisible() + && !getVisibleRect().overlaps(mRenamer->getRect())) + { + // renamer is not connected to the item we are renaming in any form so manage it manually + // TODO: consider stopping on any scroll action instead of when out of visible area + LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL; + finishRenamingItem(); + } + + // skip over LLFolderViewFolder::draw since we don't want the folder icon, label, + // and arrow for the root folder + LLView::draw(); + + mDragAndDropThisFrame = false; +} + +void LLFolderView::finishRenamingItem( void ) +{ + if(!mRenamer) + { + return; + } + if( mRenameItem ) + { + mRenameItem->rename( mRenamer->getText() ); + } + + closeRenamer(); + + // This is moved to an inventory observer in llinventorybridge.cpp, to handle updating after operation completed in AISv3 (SH-4611). + // List is re-sorted alphabetically, so scroll to make sure the selected item is visible. + //scrollToShowSelection(); +} + +void LLFolderView::closeRenamer( void ) +{ + if (mRenamer && mRenamer->getVisible()) + { + // Triggers onRenamerLost() that actually closes the renamer. + LLUI::getInstance()->removePopup(mRenamer); + } +} + +void LLFolderView::removeSelectedItems() +{ + if(getVisible() && getEnabled()) + { + // just in case we're removing the renaming item. + mRenameItem = NULL; + + // create a temporary structure which we will use to remove + // items, since the removal will futz with internal data + // structures. + std::vector items; + S32 count = mSelectedItems.size(); + if(count <= 0) return; + LLFolderViewItem* item = NULL; + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + item = *item_it; + if (item && item->isRemovable()) + { + items.push_back(item); + } + else + { + LL_DEBUGS() << "Cannot delete " << item->getName() << LL_ENDL; + return; + } + } + + // iterate through the new container. + count = items.size(); + LLUUID new_selection_id; + LLFolderViewItem* item_to_select = getNextUnselectedItem(); + + if(count == 1) + { + LLFolderViewItem* item_to_delete = items[0]; + LLFolderViewFolder* parent = item_to_delete->getParentFolder(); + if(parent) + { + if (item_to_delete->remove()) + { + // change selection on successful delete + setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus()); + } + } + arrangeAll(); + } + else if (count > 1) + { + std::vector listeners; + LLFolderViewModelItem* listener; + + setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus()); + + listeners.reserve(count); + for(S32 i = 0; i < count; ++i) + { + listener = items[i]->getViewModelItem(); + if(listener && (std::find(listeners.begin(), listeners.end(), listener) == listeners.end())) + { + listeners.push_back(listener); + } + } + listener = static_cast(listeners.at(0)); + if(listener) + { + listener->removeBatch(listeners); + } + } + arrangeAll(); + scrollToShowSelection(); + } +} + +void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) +{ + if ((mAutoOpenItems.check() == item) || + (mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) || + item->isOpen()) + { + return; + } + + // close auto-opened folders + LLFolderViewFolder* close_item = mAutoOpenItems.check(); + while (close_item && close_item != item->getParentFolder()) + { + mAutoOpenItems.pop(); + close_item->setOpenArrangeRecursively(false); + close_item = mAutoOpenItems.check(); + } + + item->requestArrange(); + + mAutoOpenItems.push(item); + + item->setOpen(true); + if(!item->isSingleFolderMode()) + { + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); + LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); + scrollToShowItem(item, constraint_rect); + } +} + +void LLFolderView::closeAutoOpenedFolders() +{ + while (mAutoOpenItems.check()) + { + LLFolderViewFolder* close_item = mAutoOpenItems.pop(); + close_item->setOpen(false); + } + + if (mAutoOpenCandidate) + { + mAutoOpenCandidate->setAutoOpenCountdown(0.f); + } + mAutoOpenCandidate = NULL; + mAutoOpenTimer.stop(); +} + +bool LLFolderView::autoOpenTest(LLFolderViewFolder* folder) +{ + if (folder && mAutoOpenCandidate == folder) + { + if (mAutoOpenTimer.getStarted()) + { + if (!mAutoOpenCandidate->isOpen()) + { + mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f)); + } + if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime) + { + autoOpenItem(folder); + mAutoOpenTimer.stop(); + return true; + } + } + return false; + } + + // otherwise new candidate, restart timer + if (mAutoOpenCandidate) + { + mAutoOpenCandidate->setAutoOpenCountdown(0.f); + } + mAutoOpenCandidate = folder; + mAutoOpenTimer.start(); + return false; +} + +bool LLFolderView::canCopy() const +{ + 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; + if (!item->getViewModelItem()->isItemCopyable()) + { + return false; + } + } + return true; +} + +// copy selected item +void LLFolderView::copy() +{ + // *NOTE: total hack to clear the inventory clipboard + LLClipboard::instance().reset(); + S32 count = mSelectedItems.size(); + if(getVisible() && getEnabled() && (count > 0)) + { + LLFolderViewModelItem* listener = NULL; + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + listener = (*item_it)->getViewModelItem(); + if(listener) + { + listener->copyToClipboard(); + } + } + } + mSearchString.clear(); +} + +bool LLFolderView::canCut() const +{ + 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 LLFolderViewModelItem* listener = item->getViewModelItem(); + + if (!listener || !listener->isItemRemovable()) + { + return false; + } + } + return true; +} + +void LLFolderView::cut() +{ + // clear the inventory clipboard + LLClipboard::instance().reset(); + if(getVisible() && getEnabled() && (mSelectedItems.size() > 0)) + { + // Find out which item will be selected once the selection will be cut + LLFolderViewItem* item_to_select = getNextUnselectedItem(); + + // Get the selection: removeItem() modified mSelectedItems and makes iterating on it unwise + std::set inventory_selected = getSelectionList(); + + // Move each item to the clipboard and out of their folder + for (std::set::iterator item_it = inventory_selected.begin(); item_it != inventory_selected.end(); ++item_it) + { + LLFolderViewItem* item_to_cut = *item_it; + LLFolderViewModelItem* listener = item_to_cut->getViewModelItem(); + if (listener) + { + listener->cutToClipboard(); + } + } + + // Update the selection + setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus()); + } + mSearchString.clear(); +} + +bool LLFolderView::canPaste() const +{ + if (mSelectedItems.empty()) + { + return false; + } + + if(getVisible() && getEnabled()) + { + for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); + item_it != mSelectedItems.end(); ++item_it) + { + // *TODO: only check folders and parent folders of items + const LLFolderViewItem* item = (*item_it); + const LLFolderViewModelItem* listener = item->getViewModelItem(); + if(!listener || !listener->isClipboardPasteable()) + { + const LLFolderViewFolder* folderp = item->getParentFolder(); + listener = folderp->getViewModelItem(); + if (!listener || !listener->isClipboardPasteable()) + { + return false; + } + } + } + return true; + } + return false; +} + +// paste selected item +void LLFolderView::paste() +{ + if(getVisible() && getEnabled()) + { + // find set of unique folders to paste into + std::set folder_set; + + selected_items_t::iterator selected_it; + for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) + { + LLFolderViewItem* item = *selected_it; + LLFolderViewFolder* folder = dynamic_cast(item); + if (folder == NULL) + { + folder = item->getParentFolder(); + } + folder_set.insert(folder); + } + + std::set::iterator set_iter; + for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) + { + LLFolderViewModelItem* listener = (*set_iter)->getViewModelItem(); + if(listener && listener->isClipboardPasteable()) + { + listener->pasteFromClipboard(); + } + } + } + mSearchString.clear(); +} + +// public rename functionality - can only start the process +void LLFolderView::startRenamingSelectedItem( void ) +{ + LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL; + + // make sure selection is visible + scrollToShowSelection(); + + S32 count = mSelectedItems.size(); + LLFolderViewItem* item = NULL; + if(count > 0) + { + item = mSelectedItems.front(); + } + if(getVisible() && getEnabled() && (count == 1) && item && item->getViewModelItem() && + item->getViewModelItem()->isItemRenameable()) + { + mRenameItem = item; + + updateRenamerPosition(); + + + mRenamer->setText(item->getName()); + mRenamer->selectAll(); + mRenamer->setVisible( true ); + // set focus will fail unless item is visible + mRenamer->setFocus( true ); + if (!mRenamerTopLostSignalConnection.connected()) + { + mRenamerTopLostSignalConnection = mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); + } + LLUI::getInstance()->addPopup(mRenamer); + } +} + +bool LLFolderView::handleKeyHere( KEY key, MASK mask ) +{ + bool handled = false; + + // 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(); + if (menu && menu->isOpen()) + { + LLMenuGL::sMenuContainer->hideMenus(); + } + + switch( key ) + { + case KEY_F2: + mSearchString.clear(); + startRenamingSelectedItem(); + handled = true; + break; + + case KEY_RETURN: + if (mask == MASK_NONE) + { + if( mRenameItem && mRenamer->getVisible() ) + { + finishRenamingItem(); + mSearchString.clear(); + handled = true; + } + } + break; + + case KEY_ESCAPE: + if( mRenameItem && mRenamer->getVisible() ) + { + closeRenamer(); + handled = true; + } + mSearchString.clear(); + break; + + case KEY_PAGE_UP: + mSearchString.clear(); + if (mScrollContainer) + { + mScrollContainer->pageUp(30); + } + handled = true; + break; + + case KEY_PAGE_DOWN: + mSearchString.clear(); + if (mScrollContainer) + { + mScrollContainer->pageDown(30); + } + handled = true; + break; + + case KEY_HOME: + mSearchString.clear(); + if (mScrollContainer) + { + mScrollContainer->goToTop(); + } + handled = true; + break; + + case KEY_END: + mSearchString.clear(); + if (mScrollContainer) + { + mScrollContainer->goToBottom(); + } + break; + + case KEY_DOWN: + if((mSelectedItems.size() > 0) && mScrollContainer) + { + LLFolderViewItem* last_selected = getCurSelectedItem(); + bool shift_select = mask & MASK_SHIFT; + // don't shift select down to children of folders (they are implicitly selected through parent) + LLFolderViewItem* next = last_selected->getNextOpenNode(!shift_select); + + if (!mKeyboardSelection || (!shift_select && (!next || next == last_selected))) + { + setSelection(last_selected, false, true); + mKeyboardSelection = true; + } + + if (shift_select) + { + if (next) + { + if (next->isSelected()) + { + // shrink selection + changeSelection(last_selected, false); + } + else if (last_selected->getParentFolder() == next->getParentFolder()) + { + // grow selection + changeSelection(next, true); + } + } + } + else + { + if( next ) + { + 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(); + handled = true; + } + break; + + case KEY_UP: + if((mSelectedItems.size() > 0) && mScrollContainer) + { + LLFolderViewItem* last_selected = mSelectedItems.back(); + bool shift_select = mask & MASK_SHIFT; + // don't shift select down to children of folders (they are implicitly selected through parent) + LLFolderViewItem* prev = last_selected->getPreviousOpenNode(!shift_select); + + if (!mKeyboardSelection || (!shift_select && prev == this)) + { + setSelection(last_selected, false, true); + mKeyboardSelection = true; + } + + if (shift_select) + { + if (prev) + { + if (prev->isSelected()) + { + // shrink selection + changeSelection(last_selected, false); + } + else if (last_selected->getParentFolder() == prev->getParentFolder()) + { + // grow selection + changeSelection(prev, true); + } + } + } + else + { + if( prev ) + { + 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 ); + } + } + scrollToShowSelection(); + mSearchString.clear(); + + handled = true; + } + break; + + case KEY_RIGHT: + if(mSelectedItems.size()) + { + LLFolderViewItem* last_selected = getCurSelectedItem(); + last_selected->setOpen( true ); + mSearchString.clear(); + handled = true; + } + break; + + case KEY_LEFT: + if(mSelectedItems.size()) + { + LLFolderViewItem* last_selected = getCurSelectedItem(); + if(last_selected && last_selected->isSingleFolderMode()) + { + handled = false; + break; + } + LLFolderViewItem* parent_folder = last_selected->getParentFolder(); + if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) + { + setSelection(parent_folder, false, true); + } + else + { + last_selected->setOpen( false ); + } + mSearchString.clear(); + scrollToShowSelection(); + handled = true; + } + break; + } + + return handled; +} + + +bool LLFolderView::handleUnicodeCharHere(llwchar uni_char) +{ + if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL + { + return false; + } + + if (uni_char > 0x7f) + { + LL_WARNS() << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << LL_ENDL; + return false; + } + + bool handled = false; + if (mParentPanel.get()->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. + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + if (menu && menu->isOpen()) + { + LLMenuGL::sMenuContainer->hideMenus(); + } + + //do text search + if (mSearchTimer.getElapsedTimeF32() > LLUI::getInstance()->mSettingGroups["config"]->getF32("TypeAheadTimeout")) + { + mSearchString.clear(); + } + mSearchTimer.reset(); + if (mSearchString.size() < 128) + { + mSearchString += uni_char; + } + search(getCurSelectedItem(), mSearchString, false); + + handled = true; + } + + return handled; +} + + +bool LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + mKeyboardSelection = false; + mSearchString.clear(); + + mParentPanel.get()->setFocus(true); + + LLEditMenuHandler::gEditMenuHandler = this; + + return LLView::handleMouseDown( x, y, mask ); +} + +bool LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, bool backward) +{ + // get first selected item + LLFolderViewItem* search_item = first_item; + + // make sure search string is upper case + std::string upper_case_string = search_string; + LLStringUtil::toUpper(upper_case_string); + + // if nothing selected, select first item in folder + if (!search_item) + { + // start from first item + search_item = getNextFromChild(NULL); + } + + // search over all open nodes for first substring match (with wrapping) + bool found = false; + LLFolderViewItem* original_search_item = search_item; + do + { + // wrap at end + if (!search_item) + { + if (backward) + { + search_item = getPreviousFromChild(NULL); + } + else + { + search_item = getNextFromChild(NULL); + } + if (!search_item || search_item == original_search_item) + { + break; + } + } + + std::string current_item_label(search_item->getViewModelItem()->getSearchableName()); + LLStringUtil::toUpper(current_item_label); + S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); + if (!current_item_label.compare(0, search_string_length, upper_case_string)) + { + found = true; + break; + } + if (backward) + { + search_item = search_item->getPreviousOpenNode(); + } + else + { + search_item = search_item->getNextOpenNode(); + } + + } while(search_item != original_search_item); + + + if (found) + { + setSelection(search_item, false, true); + scrollToShowSelection(); + } + + return found; +} + +bool LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) +{ + // skip LLFolderViewFolder::handleDoubleClick() + return LLView::handleDoubleClick( x, y, mask ); +} + +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 + mParentPanel.get()->setFocus(true); + + bool handled = childrenHandleRightMouseDown(x, y, mask) != NULL; + S32 count = mSelectedItems.size(); + + LLMenuGL* menu = static_cast(mPopupMenuHandle.get()); + if (!menu) + { + if (mCallbackRegistrar) + { + mCallbackRegistrar->pushScope(); + } + if (mEnableRegistrar) + { + mEnableRegistrar->pushScope(); + } + llassert(LLMenuGL::sMenuContainer != NULL); + menu = LLUICtrlFactory::getInstance()->createFromFile(mMenuFileName, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (!menu) + { + menu = LLUICtrlFactory::getDefaultWidget("inventory_menu"); + } + menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); + mPopupMenuHandle = menu->getHandle(); + if (mEnableRegistrar) + { + mEnableRegistrar->popScope(); + } + if (mCallbackRegistrar) + { + mCallbackRegistrar->popScope(); + } + } + + bool item_clicked{ false }; + for (const auto item : mSelectedItems) + { + item_clicked |= item->getRect().pointInRect(x, y); + } + if(!item_clicked && mSingleFolderMode) + { + clearSelection(); + } + bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected(); + if (menu && (mSingleFolderMode || (handled + && ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible + !hide_folder_menu) + { + if (mCallbackRegistrar) + { + mCallbackRegistrar->pushScope(); + } + if (mEnableRegistrar) + { + mEnableRegistrar->pushScope(); + } + + updateMenuOptions(menu); + + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, menu, x, y); + if (mEnableRegistrar) + { + mEnableRegistrar->popScope(); + } + if (mCallbackRegistrar) + { + mCallbackRegistrar->popScope(); + } + } + else + { + if (menu && menu->getVisible()) + { + menu->setVisible(false); + } + setSelection(NULL, false, true); + } + 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 ); +} + +LLFolderViewItem* LLFolderView::getHoveredItem() const +{ + return dynamic_cast(mHoveredItem.get()); +} + +void LLFolderView::setHoveredItem(LLFolderViewItem* itemp) +{ + if (mHoveredItem.get() != itemp) + { + if (itemp) + mHoveredItem = itemp->getHandle(); + else + mHoveredItem.markDead(); + } +} + +bool LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + 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) + { + handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + + return handled; +} + +void LLFolderView::deleteAllChildren() +{ + mRenamerTopLostSignalConnection.disconnect(); + if (mRenamer) + { + LLUI::getInstance()->removePopup(mRenamer); + } + if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); + mPopupMenuHandle.markDead(); + mScrollContainer = NULL; + mRenameItem = NULL; + mRenamer = NULL; + mStatusTextBox = NULL; + + clearSelection(); + LLView::deleteAllChildren(); +} + +void LLFolderView::scrollToShowSelection() +{ + if ( mSelectedItems.size() ) + { + mNeedsScroll = true; + } +} + +// If the parent is scroll container, scroll it to make the selection +// is maximally visible. +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 exists and is in visible portion of parent folder... + if(item) + { + LLRect local_rect = item->getLocalRect(); + S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); + S32 label_height = 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 ) + item->getIconPad()) : local_rect.getHeight(); + + // get portion of item that we want to see... + LLRect item_local_rect = LLRect(item->getIndentation(), + local_rect.getHeight(), + //+40 is supposed to include few first characters + llmin(item->getLabelXPos() - item->getIndentation() + 40, local_rect.getWidth()), + llmax(0, local_rect.getHeight() - max_height_to_show)); + + LLRect item_doc_rect; + + item->localRectToOtherView(item_local_rect, &item_doc_rect, this); + + mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect ); + + } +} + +LLRect LLFolderView::getVisibleRect() +{ + S32 visible_height = (mScrollContainer ? mScrollContainer->getRect().getHeight() : 0); + S32 visible_width = (mScrollContainer ? mScrollContainer->getRect().getWidth() : 0); + LLRect visible_rect; + visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); + return visible_rect; +} + +bool LLFolderView::getShowSelectionContext() +{ + if (mShowSelectionContext) + { + return true; + } + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + if (menu && menu->getVisible()) + { + return true; + } + return false; +} + +void LLFolderView::setShowSingleSelection(bool show) +{ + if (show != mShowSingleSelection) + { + mMultiSelectionFadeTimer.reset(); + mShowSingleSelection = show; + } +} + +static LLTrace::BlockTimerStatHandle FTM_INVENTORY("Inventory"); + +// Main idle routine +void LLFolderView::update() +{ + // If this is associated with the user's inventory, don't do anything + // until that inventory is loaded up. + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; //LL_RECORD_BLOCK_TIME(FTM_INVENTORY); + + // If there's no model, the view is in suspended state (being deleted) and shouldn't be updated + if (getFolderViewModel() == NULL) + { + return; + } + + LLFolderViewFilter& filter_object = getFolderViewModel()->getFilter(); + + if (filter_object.isModified() && filter_object.isNotDefault() && mParentPanel.get()->getVisible()) + { + mNeedsAutoSelect = true; + } + + // Filter to determine visibility before arranging + filter(filter_object); + + // Clear the modified setting on the filter only if the filter finished after running the filter process + // Note: if the filter count has timed out, that means the filter halted before completing the entire set of items + bool filter_modified = filter_object.isModified(); + if (filter_modified && (!filter_object.isTimedOut())) + { + filter_object.clearModified(); + } + + // automatically show matching items, and select first one if we had a selection + if (mNeedsAutoSelect) + { + // select new item only if a filtered item not currently selected and there was a selection + LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); + if (!mAutoSelectOverride && selected_itemp && !selected_itemp->getViewModelItem()->potentiallyVisible()) + { + // these are named variables to get around gcc not binding non-const references to rvalues + // and functor application is inherently non-const to allow for stateful functors + LLSelectFirstFilteredItem functor; + applyFunctorRecursively(functor); + } + + // Open filtered folders for folder views with mAutoSelectOverride=true. + // Used by LLPlacesFolderView. + if (filter_object.showAllResults()) + { + // these are named variables to get around gcc not binding non-const references to rvalues + // and functor application is inherently non-const to allow for stateful functors + LLOpenFilteredFolders functor; + applyFunctorRecursively(functor); + } + + scrollToShowSelection(); + } + + bool filter_finished = mViewModel->contentsReady() + && (getViewModelItem()->passedFilter() + || ( getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration() + && !filter_modified)); + if (filter_finished + || gFocusMgr.childHasKeyboardFocus(mParentPanel.get()) + || gFocusMgr.childHasMouseCapture(mParentPanel.get())) + { + // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process + mNeedsAutoSelect = false; + } + + bool is_visible = isInVisibleChain() || mForceArrange; + + //Puts folders/items in proper positions + // arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849) + // It also handles the open/close folder animation + if ( is_visible ) + { + sanitizeSelection(); + if( needsArrange() ) + { + S32 height = 0; + S32 width = 0; + S32 total_height = arrange( &width, &height ); + notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); + } + } + + // 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_finished) + { + // calculate rectangle to pin item to at start of animated rearrange + if (!mPinningSelectedItem && !mSelectedItems.empty()) + { + // lets pin it! + mPinningSelectedItem = true; + + //Computes visible area + const LLRect visible_content_rect = (mScrollContainer ? mScrollContainer->getVisibleContentRect() : LLRect()); + LLFolderViewItem* selected_item = mSelectedItems.back(); + + //Computes location of selected content, content outside visible area will be scrolled to using below code + LLRect item_rect; + selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this); + + //Computes intersected region of the selected content and visible area + LLRect overlap_rect(item_rect); + overlap_rect.intersectWith(visible_content_rect); + + //Don't scroll when the selected content exists within the visible area + if (overlap_rect.getHeight() >= selected_item->getItemHeight()) + { + // then attempt to keep it in same place on screen + mScrollConstraintRect = item_rect; + mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom); + } + //Scroll because the selected content is outside the visible area + else + { + // otherwise we just want it onscreen somewhere + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); + 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 ? mScrollContainer->getContentWindowRect() : LLRect()); + constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + } + + if (mSelectedItems.size() && mNeedsScroll) + { + LLFolderViewItem* scroll_to_item = mSelectedItems.back(); + scrollToShowItem(scroll_to_item, constraint_rect); + // continue scrolling until animated layout change is done + bool selected_filter_finished = getRoot()->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration(); + if (selected_filter_finished && scroll_to_item && scroll_to_item->getViewModelItem()) + { + selected_filter_finished = scroll_to_item->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration(); + } + if (filter_finished && selected_filter_finished) + { + bool needs_arrange = needsArrange() || getRoot()->needsArrange(); + if (mParentFolder) + { + needs_arrange |= (bool)mParentFolder->needsArrange(); + } + if (!needs_arrange || !is_visible) + { + mNeedsScroll = false; + } + } + } + + if (mSelectedItems.size()) + { + LLFolderViewItem* item = mSelectedItems.back(); + // If the goal is to show renamer, don't callback untill + // item is visible or is no longer being scrolled to. + // Otherwise renamer will be instantly closed + // Todo: consider moving renamer out of selection callback + if (!mNeedsAutoRename || !mNeedsScroll || item->getVisible()) + { + if (mSignalSelectCallback) + { + //RN: we use keyboard focus as a proxy for user-explicit actions + bool take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); + mSelectSignal(mSelectedItems, take_keyboard_focus); + } + mSignalSelectCallback = false; + } + } + else + { + mSignalSelectCallback = false; + } +} + +void LLFolderView::dumpSelectionInformation() +{ + LL_INFOS() << "LLFolderView::dumpSelectionInformation()" << LL_NEWLINE + << "****************************************" << LL_ENDL; + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + LL_INFOS() << " " << (*item_it)->getName() << LL_ENDL; + } + LL_INFOS() << "****************************************" << LL_ENDL; +} + +void LLFolderView::updateRenamerPosition() +{ + if(mRenameItem) + { + // See also LLFolderViewItem::draw() + S32 x = mRenameItem->getLabelXPos(); + 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, (S32)LLUI::getInstance()->getWindowSize().mV[VX], 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 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0); + U32 flags = multi_select_flag | 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 = multi_select_flag; + } + + if(mSingleFolderMode && (mSelectedItems.size() == 0)) + { + buildContextMenu(*menu, flags); + } + + // This adds a check for restrictions based on the entire + // selection set - for example, any one wearable may not push you + // over the limit, but all wearables together still might. + if (getFolderViewGroupedItemModel()) + { + getFolderViewGroupedItemModel()->groupFilterContextMenu(mSelectedItems,*menu); + } + + 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::isFolderSelected() +{ + selected_items_t::iterator item_iter; + for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) + { + LLFolderViewFolder* folder = dynamic_cast(*item_iter); + if (folder != NULL) + { + return true; + } + } + return false; +} + +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 +///---------------------------------------------------------------------------- + +void LLFolderView::onRenamerLost() +{ + if (mRenamer && mRenamer->getVisible()) + { + mRenamer->setVisible(false); + + // will commit current name (which could be same as original name) + mRenamer->setFocus(false); + } + + if( mRenameItem ) + { + setSelection( mRenameItem, true ); + mRenameItem = NULL; + } +} + +LLFolderViewItem* LLFolderView::getNextUnselectedItem() +{ + LLFolderViewItem* last_item = *mSelectedItems.rbegin(); + LLFolderViewItem* new_selection = last_item->getNextOpenNode(false); + while(new_selection && new_selection->isSelected()) + { + new_selection = new_selection->getNextOpenNode(false); + } + if (!new_selection) + { + new_selection = last_item->getPreviousOpenNode(false); + while (new_selection && (new_selection->isInSelection())) + { + new_selection = new_selection->getPreviousOpenNode(false); + } + } + return new_selection; +} + +S32 LLFolderView::getItemHeight() const +{ + if(!hasVisibleChildren()) +{ + //We need to display status textbox, let's reserve some place for it + return llmax(0, mStatusTextBox->getTextPixelHeight()); +} + return 0; +} diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index 7c3167c156..62ef2a0626 100644 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -1,448 +1,448 @@ -/** - * @file llfolderview.h - * @brief Definition of the folder view collection of classes. - * - * $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$ - */ - -/** - * - * The folder view collection of classes provides an interface for - * making a 'folder view' similar to the way the a single pane file - * folder interface works. - * - */ - -#ifndef LL_LLFOLDERVIEW_H -#define LL_LLFOLDERVIEW_H - -#include "llfolderviewitem.h" // because LLFolderView is-a LLFolderViewFolder - -#include "lluictrl.h" -#include "v4color.h" -#include "lldepthstack.h" -#include "lleditmenuhandler.h" -#include "llfontgl.h" -#include "llscrollcontainer.h" - -class LLFolderViewModelInterface; -class LLFolderViewGroupedItemModel; -class LLFolderViewFolder; -class LLFolderViewItem; -class LLFolderViewFilter; -class LLPanel; -class LLLineEditor; -class LLMenuGL; -class LLUICtrl; -class LLTextBox; - -/** - * Class LLFolderViewScrollContainer - * - * A scroll container which provides the information about the height - * of currently displayed folder view contents. - * Used for updating vertical scroll bar visibility in inventory panel. - * See LLScrollContainer::calcVisibleSize(). - */ -class LLFolderViewScrollContainer : public LLScrollContainer -{ -public: - /*virtual*/ ~LLFolderViewScrollContainer() {}; - /*virtual*/ const LLRect getScrolledViewRect() const; - -protected: - LLFolderViewScrollContainer(const LLScrollContainer::Params& p); - friend class LLUICtrlFactory; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderView -// -// The LLFolderView represents the root level folder view object. -// It manages the screen region of the folder view. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler -{ -public: - struct Params : public LLInitParam::Block - { - Mandatory parent_panel; - Optional title; - Optional use_label_suffix, - allow_multiselect, - allow_drag, - show_empty_message, - use_ellipses, - show_item_link_overlays, - suppress_folder_menu; - Mandatory view_model; - Optional grouped_item_model; - Mandatory options_menu; - - - Params(); - }; - - friend class LLFolderViewScrollContainer; - typedef folder_view_item_deque selected_items_t; - - LLFolderView(const Params&); - virtual ~LLFolderView( void ); - - virtual bool canFocusChildren() const; - - virtual const LLFolderView* getRoot() const { return this; } - virtual LLFolderView* getRoot() { return this; } - - LLFolderViewModelInterface* getFolderViewModel() { return mViewModel; } - const LLFolderViewModelInterface* getFolderViewModel() const { return mViewModel; } - - LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; } - const LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() const { return mGroupedItemModel; } - - 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); } - - bool getAllowMultiSelect() { return mAllowMultiSelect; } - bool getAllowDrag() { return mAllowDrag; } - - void setSingleFolderMode(bool is_single_mode) { mSingleFolderMode = is_single_mode; } - bool isSingleFolderMode() { return mSingleFolderMode; } - - // Close all folders in the view - void closeAllFolders(); - void openTopLevelFolders(); - - virtual void addFolder( LLFolderViewFolder* folder); - - // Find width and height of this object and its children. Also - // makes sure that this view and its children are the right size. - virtual S32 arrange( S32* width, S32* height ); - virtual S32 getItemHeight() const; - - void arrangeAll() { mArrangeGeneration++; } - S32 getArrangeGeneration() { return mArrangeGeneration; } - - // applies filters to control visibility of items - virtual void filter( LLFolderViewFilter& filter); - - void clearHoveredItem() { setHoveredItem(nullptr); } - LLFolderViewItem* getHoveredItem() const; - void setHoveredItem(LLFolderViewItem* itemp); - - // Get the last selected item - virtual LLFolderViewItem* getCurSelectedItem( void ); - selected_items_t& getSelectedItems( void ); - - // Record the selected item and pass it down the hierarchy. - virtual bool setSelection(LLFolderViewItem* selection, bool openitem, - bool take_keyboard_focus = true); - - // 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 std::set getSelectionList() const; - - // Make sure if ancestor is selected, descendants are not - void sanitizeSelection(); - virtual void clearSelection(); - void addToSelectionList(LLFolderViewItem* item); - void removeFromSelectionList(LLFolderViewItem* item); - - bool startDrag(); - void setDragAndDropThisFrame() { mDragAndDropThisFrame = true; } - void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } - LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } - - // Deletion functionality - void removeSelectedItems(); - - void autoOpenItem(LLFolderViewFolder* item); - void closeAutoOpenedFolders(); - bool autoOpenTest(LLFolderViewFolder* item); - bool isOpen() const { return true; } // root folder always open - - // Copy & paste - virtual bool canCopy() const; - virtual void copy(); - - virtual bool canCut() const; - virtual void cut(); - - virtual bool canPaste() const; - virtual void paste(); - - LLFolderViewItem* getNextUnselectedItem(); - - // Public rename functionality - can only start the process - void startRenamingSelectedItem( void ); - - // LLView functionality - ///*virtual*/ bool handleKey( KEY key, MASK mask, bool called_from_parent ); - /*virtual*/ bool handleKeyHere( KEY key, MASK mask ); - /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); - /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ bool handleDoubleClick( S32 x, S32 y, MASK mask ); - /*virtual*/ bool handleRightMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ bool handleHover( 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); - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(false); } - virtual void draw(); - virtual void deleteAllChildren(); - - void stopAutoScollining() {mNeedsScroll = false;} - void scrollToShowSelection(); - void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); - void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } - LLRect getVisibleRect(); - - bool search(LLFolderViewItem* first_item, const std::string &search_string, bool backward); - void setShowSelectionContext(bool show) { mShowSelectionContext = show; } - bool getShowSelectionContext(); - void setShowSingleSelection(bool show); - bool getShowSingleSelection() { return mShowSingleSelection; } - F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } - bool getUseEllipses() { return mUseEllipses; } - S32 getSelectedCount() { return (S32)mSelectedItems.size(); } - - void update(); // needs to be called periodically (e.g. once per frame) - - 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 showItemLinkOverlays() { return mShowItemLinkOverlays; } - - void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } - void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; } - - void setForceArrange(bool force) { mForceArrange = force; } - - LLPanel* getParentPanel() { return mParentPanel.get(); } - // DEBUG only - void dumpSelectionInformation(); - - virtual S32 notify(const LLSD& info) ; - - void setShowEmptyMessage(bool show_msg) { mShowEmptyMessage = show_msg; } - - bool useLabelSuffix() { return mUseLabelSuffix; } - virtual void updateMenu(); - - void finishRenamingItem( void ); - - // Note: We may eventually have to move that method up the hierarchy to LLFolderViewItem. - LLHandle getHandle() const { return getDerivedHandle(); } - -private: - void updateMenuOptions(LLMenuGL* menu); - void updateRenamerPosition(); - -protected: - LLScrollContainer* mScrollContainer; // NULL if this is not a child of a scroll container. - - void commitRename( const LLSD& data ); - void onRenamerLost(); - - void closeRenamer( void ); - - bool isFolderSelected(); - - bool selectFirstItem(); - bool selectLastItem(); - - bool addNoOptions(LLMenuGL* menu) const; - - -protected: - LLHandle mPopupMenuHandle; - std::string mMenuFileName; - - LLHandle mHoveredItem; - selected_items_t mSelectedItems; - bool mKeyboardSelection, - mAllowMultiSelect, - mAllowDrag, - mShowEmptyMessage, - mShowFolderHierarchy, - mNeedsScroll, - mPinningSelectedItem, - mNeedsAutoSelect, - mAutoSelectOverride, - mNeedsAutoRename, - mUseLabelSuffix, - mDragAndDropThisFrame, - mShowItemLinkOverlays, - mShowSelectionContext, - mShowSingleSelection, - mSuppressFolderMenu, - mSingleFolderMode; - - // Renaming variables and methods - LLFolderViewItem* mRenameItem; // The item currently being renamed - LLLineEditor* mRenamer; - - LLRect mScrollConstraintRect; - - LLDepthStack mAutoOpenItems; - LLFolderViewFolder* mAutoOpenCandidate; - LLFrameTimer mAutoOpenTimer; - LLFrameTimer mSearchTimer; - std::string mSearchString; - LLFrameTimer mMultiSelectionFadeTimer; - S32 mArrangeGeneration; - - signal_t mSelectSignal; - signal_t mReshapeSignal; - S32 mSignalSelectCallback; - S32 mMinWidth; - - LLHandle mParentPanel; - - LLFolderViewModelInterface* mViewModel; - LLFolderViewGroupedItemModel* mGroupedItemModel; - - /** - * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. - * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel. - */ - bool mUseEllipses; // See EXT-719 - - /** - * Contains item under mouse pointer while dragging - */ - LLFolderViewItem* mDraggingOverItem; // See EXT-719 - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar; - - boost::signals2::connection mRenamerTopLostSignalConnection; - - bool mForceArrange; - -public: - static F32 sAutoOpenTime; - LLTextBox* mStatusTextBox; - -}; - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewFunctor -// -// Simple abstract base class for applying a functor to folders and -// items in a folder view hierarchy. This is suboptimal for algorithms -// that only work folders or only work on items, but I'll worry about -// that later when it's determined to be too slow. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLFolderViewFunctor -{ -public: - virtual ~LLFolderViewFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder) = 0; - virtual void doItem(LLFolderViewItem* item) = 0; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLSelectFirstFilteredItem -// -// This will select the first *item* found in the hierarchy. If no item can be -// selected, the first matching folder will. -// Since doFolder() is done first but we prioritize item selection, we let the -// first filtered folder set the selection and raise a folder flag. -// The selection might be overridden by the first filtered item in doItem() -// which checks an item flag. Since doFolder() checks the item flag too, the first -// item will still be selected if items were to be done first and folders second. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLSelectFirstFilteredItem : public LLFolderViewFunctor -{ -public: - LLSelectFirstFilteredItem() : mItemSelected(false), mFolderSelected(false) {} - virtual ~LLSelectFirstFilteredItem() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - bool wasItemSelected() { return mItemSelected || mFolderSelected; } -protected: - bool mItemSelected; - bool mFolderSelected; -}; - -class LLOpenFilteredFolders : public LLFolderViewFunctor -{ -public: - LLOpenFilteredFolders() {} - virtual ~LLOpenFilteredFolders() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); -}; - -class LLSaveFolderState : public LLFolderViewFunctor -{ -public: - LLSaveFolderState() : mApply(false) {} - virtual ~LLSaveFolderState() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item) {} - void setApply(bool apply); - void clearOpenFolders() { mOpenFolders.clear(); } -protected: - std::set mOpenFolders; - bool mApply; -}; - -class LLOpenFoldersWithSelection : public LLFolderViewFunctor -{ -public: - LLOpenFoldersWithSelection() {} - virtual ~LLOpenFoldersWithSelection() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); -}; - -class LLAllDescendentsPassedFilter : public LLFolderViewFunctor -{ -public: - LLAllDescendentsPassedFilter() : mAllDescendentsPassedFilter(true) {} - /*virtual*/ ~LLAllDescendentsPassedFilter() {} - /*virtual*/ void doFolder(LLFolderViewFolder* folder); - /*virtual*/ void doItem(LLFolderViewItem* item); - bool allDescendentsPassedFilter() const { return mAllDescendentsPassedFilter; } -protected: - bool mAllDescendentsPassedFilter; -}; - -// Flags for buildContextMenu() -const U32 SUPPRESS_OPEN_ITEM = 0x1; -const U32 FIRST_SELECTED_ITEM = 0x2; -const U32 ITEM_IN_MULTI_SELECTION = 0x4; - -#endif // LL_LLFOLDERVIEW_H +/** + * @file llfolderview.h + * @brief Definition of the folder view collection of classes. + * + * $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$ + */ + +/** + * + * The folder view collection of classes provides an interface for + * making a 'folder view' similar to the way the a single pane file + * folder interface works. + * + */ + +#ifndef LL_LLFOLDERVIEW_H +#define LL_LLFOLDERVIEW_H + +#include "llfolderviewitem.h" // because LLFolderView is-a LLFolderViewFolder + +#include "lluictrl.h" +#include "v4color.h" +#include "lldepthstack.h" +#include "lleditmenuhandler.h" +#include "llfontgl.h" +#include "llscrollcontainer.h" + +class LLFolderViewModelInterface; +class LLFolderViewGroupedItemModel; +class LLFolderViewFolder; +class LLFolderViewItem; +class LLFolderViewFilter; +class LLPanel; +class LLLineEditor; +class LLMenuGL; +class LLUICtrl; +class LLTextBox; + +/** + * Class LLFolderViewScrollContainer + * + * A scroll container which provides the information about the height + * of currently displayed folder view contents. + * Used for updating vertical scroll bar visibility in inventory panel. + * See LLScrollContainer::calcVisibleSize(). + */ +class LLFolderViewScrollContainer : public LLScrollContainer +{ +public: + /*virtual*/ ~LLFolderViewScrollContainer() {}; + /*virtual*/ const LLRect getScrolledViewRect() const; + +protected: + LLFolderViewScrollContainer(const LLScrollContainer::Params& p); + friend class LLUICtrlFactory; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFolderView +// +// The LLFolderView represents the root level folder view object. +// It manages the screen region of the folder view. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler +{ +public: + struct Params : public LLInitParam::Block + { + Mandatory parent_panel; + Optional title; + Optional use_label_suffix, + allow_multiselect, + allow_drag, + show_empty_message, + use_ellipses, + show_item_link_overlays, + suppress_folder_menu; + Mandatory view_model; + Optional grouped_item_model; + Mandatory options_menu; + + + Params(); + }; + + friend class LLFolderViewScrollContainer; + typedef folder_view_item_deque selected_items_t; + + LLFolderView(const Params&); + virtual ~LLFolderView( void ); + + virtual bool canFocusChildren() const; + + virtual const LLFolderView* getRoot() const { return this; } + virtual LLFolderView* getRoot() { return this; } + + LLFolderViewModelInterface* getFolderViewModel() { return mViewModel; } + const LLFolderViewModelInterface* getFolderViewModel() const { return mViewModel; } + + LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; } + const LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() const { return mGroupedItemModel; } + + 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); } + + bool getAllowMultiSelect() { return mAllowMultiSelect; } + bool getAllowDrag() { return mAllowDrag; } + + void setSingleFolderMode(bool is_single_mode) { mSingleFolderMode = is_single_mode; } + bool isSingleFolderMode() { return mSingleFolderMode; } + + // Close all folders in the view + void closeAllFolders(); + void openTopLevelFolders(); + + virtual void addFolder( LLFolderViewFolder* folder); + + // Find width and height of this object and its children. Also + // makes sure that this view and its children are the right size. + virtual S32 arrange( S32* width, S32* height ); + virtual S32 getItemHeight() const; + + void arrangeAll() { mArrangeGeneration++; } + S32 getArrangeGeneration() { return mArrangeGeneration; } + + // applies filters to control visibility of items + virtual void filter( LLFolderViewFilter& filter); + + void clearHoveredItem() { setHoveredItem(nullptr); } + LLFolderViewItem* getHoveredItem() const; + void setHoveredItem(LLFolderViewItem* itemp); + + // Get the last selected item + virtual LLFolderViewItem* getCurSelectedItem( void ); + selected_items_t& getSelectedItems( void ); + + // Record the selected item and pass it down the hierarchy. + virtual bool setSelection(LLFolderViewItem* selection, bool openitem, + bool take_keyboard_focus = true); + + // 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 std::set getSelectionList() const; + + // Make sure if ancestor is selected, descendants are not + void sanitizeSelection(); + virtual void clearSelection(); + void addToSelectionList(LLFolderViewItem* item); + void removeFromSelectionList(LLFolderViewItem* item); + + bool startDrag(); + void setDragAndDropThisFrame() { mDragAndDropThisFrame = true; } + void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } + LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } + + // Deletion functionality + void removeSelectedItems(); + + void autoOpenItem(LLFolderViewFolder* item); + void closeAutoOpenedFolders(); + bool autoOpenTest(LLFolderViewFolder* item); + bool isOpen() const { return true; } // root folder always open + + // Copy & paste + virtual bool canCopy() const; + virtual void copy(); + + virtual bool canCut() const; + virtual void cut(); + + virtual bool canPaste() const; + virtual void paste(); + + LLFolderViewItem* getNextUnselectedItem(); + + // Public rename functionality - can only start the process + void startRenamingSelectedItem( void ); + + // LLView functionality + ///*virtual*/ bool handleKey( KEY key, MASK mask, bool called_from_parent ); + /*virtual*/ bool handleKeyHere( KEY key, MASK mask ); + /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); + /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleDoubleClick( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleRightMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleHover( 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); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(false); } + virtual void draw(); + virtual void deleteAllChildren(); + + void stopAutoScollining() {mNeedsScroll = false;} + void scrollToShowSelection(); + void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); + void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } + LLRect getVisibleRect(); + + bool search(LLFolderViewItem* first_item, const std::string &search_string, bool backward); + void setShowSelectionContext(bool show) { mShowSelectionContext = show; } + bool getShowSelectionContext(); + void setShowSingleSelection(bool show); + bool getShowSingleSelection() { return mShowSingleSelection; } + F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } + bool getUseEllipses() { return mUseEllipses; } + S32 getSelectedCount() { return (S32)mSelectedItems.size(); } + + void update(); // needs to be called periodically (e.g. once per frame) + + 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 showItemLinkOverlays() { return mShowItemLinkOverlays; } + + void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } + void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; } + + void setForceArrange(bool force) { mForceArrange = force; } + + LLPanel* getParentPanel() { return mParentPanel.get(); } + // DEBUG only + void dumpSelectionInformation(); + + virtual S32 notify(const LLSD& info) ; + + void setShowEmptyMessage(bool show_msg) { mShowEmptyMessage = show_msg; } + + bool useLabelSuffix() { return mUseLabelSuffix; } + virtual void updateMenu(); + + void finishRenamingItem( void ); + + // Note: We may eventually have to move that method up the hierarchy to LLFolderViewItem. + LLHandle getHandle() const { return getDerivedHandle(); } + +private: + void updateMenuOptions(LLMenuGL* menu); + void updateRenamerPosition(); + +protected: + LLScrollContainer* mScrollContainer; // NULL if this is not a child of a scroll container. + + void commitRename( const LLSD& data ); + void onRenamerLost(); + + void closeRenamer( void ); + + bool isFolderSelected(); + + bool selectFirstItem(); + bool selectLastItem(); + + bool addNoOptions(LLMenuGL* menu) const; + + +protected: + LLHandle mPopupMenuHandle; + std::string mMenuFileName; + + LLHandle mHoveredItem; + selected_items_t mSelectedItems; + bool mKeyboardSelection, + mAllowMultiSelect, + mAllowDrag, + mShowEmptyMessage, + mShowFolderHierarchy, + mNeedsScroll, + mPinningSelectedItem, + mNeedsAutoSelect, + mAutoSelectOverride, + mNeedsAutoRename, + mUseLabelSuffix, + mDragAndDropThisFrame, + mShowItemLinkOverlays, + mShowSelectionContext, + mShowSingleSelection, + mSuppressFolderMenu, + mSingleFolderMode; + + // Renaming variables and methods + LLFolderViewItem* mRenameItem; // The item currently being renamed + LLLineEditor* mRenamer; + + LLRect mScrollConstraintRect; + + LLDepthStack mAutoOpenItems; + LLFolderViewFolder* mAutoOpenCandidate; + LLFrameTimer mAutoOpenTimer; + LLFrameTimer mSearchTimer; + std::string mSearchString; + LLFrameTimer mMultiSelectionFadeTimer; + S32 mArrangeGeneration; + + signal_t mSelectSignal; + signal_t mReshapeSignal; + S32 mSignalSelectCallback; + S32 mMinWidth; + + LLHandle mParentPanel; + + LLFolderViewModelInterface* mViewModel; + LLFolderViewGroupedItemModel* mGroupedItemModel; + + /** + * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. + * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel. + */ + bool mUseEllipses; // See EXT-719 + + /** + * Contains item under mouse pointer while dragging + */ + LLFolderViewItem* mDraggingOverItem; // See EXT-719 + + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar; + + boost::signals2::connection mRenamerTopLostSignalConnection; + + bool mForceArrange; + +public: + static F32 sAutoOpenTime; + LLTextBox* mStatusTextBox; + +}; + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFolderViewFunctor +// +// Simple abstract base class for applying a functor to folders and +// items in a folder view hierarchy. This is suboptimal for algorithms +// that only work folders or only work on items, but I'll worry about +// that later when it's determined to be too slow. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFolderViewFunctor +{ +public: + virtual ~LLFolderViewFunctor() {} + virtual void doFolder(LLFolderViewFolder* folder) = 0; + virtual void doItem(LLFolderViewItem* item) = 0; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLSelectFirstFilteredItem +// +// This will select the first *item* found in the hierarchy. If no item can be +// selected, the first matching folder will. +// Since doFolder() is done first but we prioritize item selection, we let the +// first filtered folder set the selection and raise a folder flag. +// The selection might be overridden by the first filtered item in doItem() +// which checks an item flag. Since doFolder() checks the item flag too, the first +// item will still be selected if items were to be done first and folders second. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLSelectFirstFilteredItem : public LLFolderViewFunctor +{ +public: + LLSelectFirstFilteredItem() : mItemSelected(false), mFolderSelected(false) {} + virtual ~LLSelectFirstFilteredItem() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); + bool wasItemSelected() { return mItemSelected || mFolderSelected; } +protected: + bool mItemSelected; + bool mFolderSelected; +}; + +class LLOpenFilteredFolders : public LLFolderViewFunctor +{ +public: + LLOpenFilteredFolders() {} + virtual ~LLOpenFilteredFolders() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); +}; + +class LLSaveFolderState : public LLFolderViewFunctor +{ +public: + LLSaveFolderState() : mApply(false) {} + virtual ~LLSaveFolderState() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item) {} + void setApply(bool apply); + void clearOpenFolders() { mOpenFolders.clear(); } +protected: + std::set mOpenFolders; + bool mApply; +}; + +class LLOpenFoldersWithSelection : public LLFolderViewFunctor +{ +public: + LLOpenFoldersWithSelection() {} + virtual ~LLOpenFoldersWithSelection() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); +}; + +class LLAllDescendentsPassedFilter : public LLFolderViewFunctor +{ +public: + LLAllDescendentsPassedFilter() : mAllDescendentsPassedFilter(true) {} + /*virtual*/ ~LLAllDescendentsPassedFilter() {} + /*virtual*/ void doFolder(LLFolderViewFolder* folder); + /*virtual*/ void doItem(LLFolderViewItem* item); + bool allDescendentsPassedFilter() const { return mAllDescendentsPassedFilter; } +protected: + bool mAllDescendentsPassedFilter; +}; + +// Flags for buildContextMenu() +const U32 SUPPRESS_OPEN_ITEM = 0x1; +const U32 FIRST_SELECTED_ITEM = 0x2; +const U32 ITEM_IN_MULTI_SELECTION = 0x4; + +#endif // LL_LLFOLDERVIEW_H diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 2bbf23c4bf..a0c7407b06 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -1,2368 +1,2368 @@ -/** -* @file llfolderviewitem.cpp -* @brief Items and folders that can appear in a hierarchical folder view -* -* $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 "../newview/llviewerprecompiledheaders.h" - -#include "llflashtimer.h" - -#include "linden_common.h" -#include "llfolderviewitem.h" -#include "llfolderview.h" -#include "llfolderviewmodel.h" -#include "llpanel.h" -#include "llcallbacklist.h" -#include "llcriticaldamp.h" -#include "llclipboard.h" -#include "llfocusmgr.h" // gFocusMgr -#include "lltrans.h" -#include "llwindow.h" - -///---------------------------------------------------------------------------- -/// Class LLFolderViewItem -///---------------------------------------------------------------------------- - -static LLDefaultChildRegistry::Register r("folder_view_item"); - -// statics -std::map LLFolderViewItem::sFonts; // map of styles to fonts - -bool LLFolderViewItem::sColorSetInitialized = false; -LLUIColor LLFolderViewItem::sFgColor; -LLUIColor LLFolderViewItem::sHighlightBgColor; -LLUIColor LLFolderViewItem::sFlashBgColor; -LLUIColor LLFolderViewItem::sFocusOutlineColor; -LLUIColor LLFolderViewItem::sMouseOverColor; -LLUIColor LLFolderViewItem::sFilterBGColor; -LLUIColor LLFolderViewItem::sFilterTextColor; -LLUIColor LLFolderViewItem::sSuffixColor; -LLUIColor LLFolderViewItem::sSearchStatusColor; - -// 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; - -const LLColor4U DEFAULT_WHITE(255, 255, 255); - - -//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::getFontDefault(); - } - sFonts[style] = rtn; - } - return rtn; -} - -//static -void LLFolderViewItem::initClass() -{ -} - -//static -void LLFolderViewItem::cleanupClass() -{ - sFonts.clear(); -} - - -// NOTE: Optimize this, we call it a *lot* when opening a large inventory -LLFolderViewItem::Params::Params() -: root(), - listener(), - folder_arrow_image("folder_arrow_image"), - folder_indentation("folder_indentation"), - selection_image("selection_image"), - item_height("item_height"), - item_top_pad("item_top_pad"), - creation_date(), - allow_wear("allow_wear", true), - allow_drop("allow_drop", true), - font_color("font_color"), - font_highlight_color("font_highlight_color"), - left_pad("left_pad", 0), - icon_pad("icon_pad", 0), - icon_width("icon_width", 0), - text_pad("text_pad", 0), - text_pad_right("text_pad_right", 0), - single_folder_mode("single_folder_mode", false), - double_click_override("double_click_override", false), - arrow_size("arrow_size", 0), - max_folder_item_overlap("max_folder_item_overlap", 0) -{ } - -// Default constructor -LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) -: LLView(p), - mLabelWidth(0), - mLabelWidthDirty(false), - mSuffixNeedsRefresh(false), - mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT), - mParentFolder( NULL ), - mIsSelected( false ), - mIsCurSelection( false ), - mSelectPending(false), - mIsItemCut(false), - mCutGeneration(0), - mLabelStyle( LLFontGL::NORMAL ), - mHasVisibleChildren(false), - mLocalIndentation(p.folder_indentation), - mIndentation(0), - mItemHeight(p.item_height), - mControlLabelRotation(0.f), - mDragAndDropTarget(false), - mLabel(p.name), - mRoot(p.root), - mViewModelItem(p.listener), - mIsMouseOverTitle(false), - mAllowWear(p.allow_wear), - mAllowDrop(p.allow_drop), - mFontColor(p.font_color), - mFontHighlightColor(p.font_highlight_color), - mLeftPad(p.left_pad), - mIconPad(p.icon_pad), - mIconWidth(p.icon_width), - mTextPad(p.text_pad), - mTextPadRight(p.text_pad_right), - mArrowSize(p.arrow_size), - mSingleFolderMode(p.single_folder_mode), - mMaxFolderItemOverlap(p.max_folder_item_overlap), - mDoubleClickOverride(p.double_click_override) -{ - if (!sColorSetInitialized) - { - sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE); - sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); - sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); - sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); - sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); - sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); - sColorSetInitialized = true; - } - - if (mViewModelItem) - { - mViewModelItem->setFolderViewItem(this); - } -} - -// Destroys the object -LLFolderViewItem::~LLFolderViewItem() -{ - mViewModelItem = NULL; - gFocusMgr.removeKeyboardFocusWithoutCallback(this); -} - -bool LLFolderViewItem::postBuild() -{ - LLFolderViewModelItem* vmi = getViewModelItem(); - llassert(vmi); // not supposed to happen, if happens, find out why and fix - if (vmi) - { - // getDisplayName() is expensive (due to internal getLabelSuffix() and name building) - // it also sets search strings so it requires a filter reset - mLabel = vmi->getDisplayName(); - setToolTip(vmi->getName()); - - // Dirty the filter flag of the model from the view (CHUI-849) - vmi->dirtyFilter(); - } - - // Don't do full refresh on constructor if it is possible to avoid - // it significantly slows down bulk view creation. - // Todo: Ideally we need to move getDisplayName() out of constructor as well. - // Like: make a logic that will let filter update search string, - // while LLFolderViewItem::arrange() updates visual part - mSuffixNeedsRefresh = true; - mLabelWidthDirty = true; - return true; -} - -LLFolderView* LLFolderViewItem::getRoot() -{ - return mRoot; -} - -const LLFolderView* LLFolderViewItem::getRoot() const -{ - return mRoot; -} -// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. -bool LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) -{ - LLFolderViewItem* root = this; - while( root->mParentFolder ) - { - if( root->mParentFolder == potential_ancestor ) - { - return true; - } - root = root->mParentFolder; - } - return false; -} - -LLFolderViewItem* LLFolderViewItem::getNextOpenNode(bool include_children) -{ - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children ); - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit last item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } - - return itemp; -} - -LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(bool include_children) -{ - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children ); - - // Skip over items that are invisible or are hidden from the UI. - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit first item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } - - return itemp; -} - -bool LLFolderViewItem::passedFilter(S32 filter_generation) -{ - return getViewModelItem()->passedFilter(filter_generation); -} - -bool LLFolderViewItem::isPotentiallyVisible(S32 filter_generation) -{ - if (filter_generation < 0) - { - filter_generation = getFolderViewModel()->getFilter().getFirstSuccessGeneration(); - } - LLFolderViewModelItem* model = getViewModelItem(); - bool visible = model->passedFilter(filter_generation); - if (model->getMarkedDirtyGeneration() >= filter_generation) - { - // unsure visibility state - // retaining previous visibility until item is updated or filter generation changes - visible |= getVisible(); - } - return visible; -} - -void LLFolderViewItem::refresh() -{ - LLFolderViewModelItem& vmi = *getViewModelItem(); - - mLabel = vmi.getDisplayName(); - setToolTip(vmi.getName()); - // icons are slightly expensive to get, can be optimized - // see LLInventoryIcon::getIcon() - mIcon = vmi.getIcon(); - mIconOpen = vmi.getIconOpen(); - mIconOverlay = vmi.getIconOverlay(); - - if (mRoot->useLabelSuffix()) - { - // Very Expensive! - // Can do a number of expensive checks, like checking active motions, wearables or friend list - mLabelStyle = vmi.getLabelStyle(); - mLabelSuffix = vmi.getLabelSuffix(); - } - - // Dirty the filter flag of the model from the view (CHUI-849) - vmi.dirtyFilter(); - - mLabelWidthDirty = true; - mSuffixNeedsRefresh = false; -} - -void LLFolderViewItem::refreshSuffix() -{ - LLFolderViewModelItem const* vmi = getViewModelItem(); - - // icons are slightly expensive to get, can be optimized - // see LLInventoryIcon::getIcon() - mIcon = vmi->getIcon(); - mIconOpen = vmi->getIconOpen(); - mIconOverlay = vmi->getIconOverlay(); - - if (mRoot->useLabelSuffix()) - { - // Very Expensive! - // Can do a number of expensive checks, like checking active motions, wearables or friend list - mLabelStyle = vmi->getLabelStyle(); - mLabelSuffix = vmi->getLabelSuffix(); - } - - mLabelWidthDirty = true; - mSuffixNeedsRefresh = false; -} - -// Utility function for LLFolderView -void LLFolderViewItem::arrangeAndSet(bool set_selection, - bool take_keyboard_focus) -{ - LLFolderView* root = getRoot(); - if (getParentFolder()) - { - getParentFolder()->requestArrange(); - } - if(set_selection) - { - getRoot()->setSelection(this, true, take_keyboard_focus); - if(root) - { - root->scrollToShowSelection(); - } - } -} - - -std::set LLFolderViewItem::getSelectionList() const -{ - std::set selection; - return selection; -} - -// addToFolder() returns true if it succeeds. false otherwise -void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder) -{ - folder->addItem(this); - - // Compute indentation since parent folder changed - mIndentation = (getParentFolder()) - ? getParentFolder()->getIndentation() + mLocalIndentation - : 0; -} - - -// Finds width and height of this object and its children. Also -// makes sure that this view and its children are the right size. -S32 LLFolderViewItem::arrange( S32* width, S32* height ) -{ - // Only indent deeper items in hierarchy - mIndentation = (getParentFolder()) - ? getParentFolder()->getIndentation() + mLocalIndentation - : 0; - if (mLabelWidthDirty) - { - if (mSuffixNeedsRefresh) - { - // Expensive. But despite refreshing label, - // it is purely visual, so it is fine to do at our laisure - refreshSuffix(); - } - mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(LLFontGL::NORMAL)->getWidth(mLabelSuffix) + mLabelPaddingRight; - mLabelWidthDirty = false; - } - - *width = llmax(*width, mLabelWidth); - - // 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() const -{ - return mItemHeight; -} - -S32 LLFolderViewItem::getLabelXPos() -{ - return getIndentation() + mArrowSize + mTextPad + mIconWidth + mIconPad; -} - -S32 LLFolderViewItem::getIconPad() -{ - return mIconPad; -} - -S32 LLFolderViewItem::getTextPad() -{ - return mTextPad; -} - -// *TODO: This can be optimized a lot by simply recording that it is -// selected in the appropriate places, and assuming that set selection -// means 'deselect' for a leaf item. Do this optimization after -// multiple selection is implemented to make sure it all plays nice -// together. -bool LLFolderViewItem::setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus) -{ - if (selection == this && !mIsSelected) - { - selectItem(); - } - else if (mIsSelected) // Deselect everything else. - { - deselectItem(); - } - return mIsSelected; -} - -bool LLFolderViewItem::changeSelection(LLFolderViewItem* selection, bool selected) -{ - if (selection == this) - { - if (mIsSelected) - { - deselectItem(); - } - else - { - selectItem(); - } - return true; - } - return false; -} - -void LLFolderViewItem::deselectItem(void) -{ - mIsSelected = false; -} - -void LLFolderViewItem::selectItem(void) -{ - if (!mIsSelected) - { - mIsSelected = true; - getViewModelItem()->selectItem(); - } -} - -bool LLFolderViewItem::isMovable() -{ - return getViewModelItem()->isItemMovable(); -} - -bool LLFolderViewItem::isRemovable() -{ - return getViewModelItem()->isItemRemovable(); -} - -void LLFolderViewItem::destroyView() -{ - getRoot()->removeFromSelectionList(this); - - if (mParentFolder) - { - // removeView deletes me - mParentFolder->extractItem(this); - } - delete this; -} - -// Call through to the viewed object and return true if it can be -// removed. -//bool LLFolderViewItem::removeRecursively(bool single_item) -bool LLFolderViewItem::remove() -{ - if(!isRemovable()) - { - return false; - } - return getViewModelItem()->removeItem(); -} - -// Build an appropriate context menu for the item. -void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - getViewModelItem()->buildContextMenu(menu, flags); -} - -void LLFolderViewItem::openItem( void ) -{ - if (mAllowWear || !getViewModelItem()->isItemWearable()) - { - getViewModelItem()->openItem(); - } -} - -void LLFolderViewItem::rename(const std::string& new_name) -{ - if( !new_name.empty() ) - { - getViewModelItem()->renameItem(new_name); - } -} - -const std::string& LLFolderViewItem::getName( void ) const -{ - static const std::string noName(""); - return getViewModelItem() ? getViewModelItem()->getName() : noName; -} - -// LLView functionality -bool LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - if(!mIsSelected) - { - getRoot()->setSelection(this, false); - } - make_ui_sound("UISndClick"); - return true; -} - -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 ); - - if (!mIsSelected) - { - if(mask & MASK_CONTROL) - { - getRoot()->changeSelection(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) - { - getParentFolder()->extendSelectionTo(this); - } - else - { - getRoot()->setSelection(this, false); - } - make_ui_sound("UISndClick"); - } - else - { - // If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. - // This is necessary so we maintain selection consistent when starting a drag. - mSelectPending = true; - } - - mDragStartX = x; - mDragStartY = y; - return true; -} - -bool LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) -{ - mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); - - if( hasMouseCapture() && isMovable() ) - { - LLFolderView* root = getRoot(); - - if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD - && root->getAllowDrag() - && root->getCurSelectedItem() - && root->startDrag()) - { - // RN: when starting drag and drop, clear out last auto-open - root->autoOpenTest(NULL); - root->setShowSelectionContext(true); - - // Release keyboard focus, so that if stuff is dropped into the - // world, pressing the delete key won't blow away the inventory - // item. - gFocusMgr.setKeyboardFocus(NULL); - - getWindow()->setCursor(UI_CURSOR_ARROW); - } - else if (x != mDragStartX || y != mDragStartY) - { - getWindow()->setCursor(UI_CURSOR_NOLOCKED); - } - - root->clearHoveredItem(); - return true; - } - else - { - LLFolderView* pRoot = getRoot(); - pRoot->setHoveredItem(this); - pRoot->setShowSelectionContext(false); - getWindow()->setCursor(UI_CURSOR_ARROW); - // let parent handle this then... - return false; - } -} - - -bool LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - openItem(); - return true; -} - -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 ) - { - //...then select - if(mask & MASK_CONTROL) - { - getRoot()->changeSelection(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) - { - getParentFolder()->extendSelectionTo(this); - } - else - { - getRoot()->setSelection(this, false); - } - } - - mSelectPending = false; - - if( hasMouseCapture() ) - { - if (getRoot()) - { - getRoot()->setShowSelectionContext(false); - } - gFocusMgr.setMouseCapture( NULL ); - } - return true; -} - -void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask) -{ - mIsMouseOverTitle = false; - - // NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it - LLFolderView* pRoot = getRoot(); - if (this == pRoot->getHoveredItem()) - { - pRoot->clearHoveredItem(); - } -} - -bool LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - bool handled = false; - bool accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); - handled = accepted; - if (accepted) - { - mDragAndDropTarget = true; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - 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) - { - LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFolderViewItem" << LL_ENDL; - } - - return handled; -} - -void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color) -{ - //--------------------------------------------------------------------------------// - // Draw open folder arrow - // - const S32 TOP_PAD = default_params.item_top_pad; - - if (hasVisibleChildren() || !isFolderComplete()) - { - LLUIImage* arrow_image = default_params.folder_arrow_image; - gl_draw_scaled_rotated_image( - mIndentation, getRect().getHeight() - mArrowSize - mTextPad - TOP_PAD, - mArrowSize, mArrowSize, mControlLabelRotation, arrow_image->getImage(), fg_color); - } -} - -/*virtual*/ bool LLFolderViewItem::isHighlightAllowed() -{ - return mIsSelected; -} - -/*virtual*/ bool LLFolderViewItem::isHighlightActive() -{ - return mIsCurSelection; -} - -/*virtual*/ bool LLFolderViewItem::isFadeItem() -{ - LLClipboard& clipboard = LLClipboard::instance(); - if (mCutGeneration != clipboard.getGeneration()) - { - mCutGeneration = clipboard.getGeneration(); - mIsItemCut = clipboard.isCutMode() - && ((getParentFolder() && getParentFolder()->isFadeItem()) - || getViewModelItem()->isCutToClipboard()); - } - return mIsItemCut; -} - -void LLFolderViewItem::drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, - const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor) -{ - const S32 focus_top = getRect().getHeight(); - const S32 focus_bottom = getRect().getHeight() - mItemHeight; - const bool folder_open = (getRect().getHeight() > mItemHeight + 4); - const S32 FOCUS_LEFT = 1; - - // Determine which background color to use for highlighting - LLUIColor bgColor = (isFlashing() ? flashColor : selectColor); - - //--------------------------------------------------------------------------------// - // Draw highlight for selected items - // Note: Always render "current" item or flashing item, only render other selected - // items if mShowSingleSelection is false. - // - if (isHighlightAllowed()) - - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - // Highlight for selected but not current items - if (!isHighlightActive() && !isFlashing()) - { - LLColor4 bg_color = bgColor; - // do time-based fade of extra objects - F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f); - if (getRoot() && getRoot()->getShowSingleSelection()) - { - // fading out - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); - } - else - { - // fading in - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); - } - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - bg_color, hasKeyboardFocus); - } - - // Highlight for currently selected or flashing item - if (isHighlightActive()) - { - // Background - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - bgColor, hasKeyboardFocus); - // Outline - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - focusOutlineColor, false); - } - - if (folder_open) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - focusOutlineColor, false); - if (showContent && !isFlashing()) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, - getRect().getWidth() - 2, - 0, - bgColor, true); - } - } - } - else if (mIsMouseOverTitle) - { - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - mouseOverColor, false); - } - - //--------------------------------------------------------------------------------// - // Draw DragNDrop highlight - // - if (mDragAndDropTarget) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - bgColor, false); - if (folder_open) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - bgColor, false); - } - mDragAndDropTarget = false; - } -} - -void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x) -{ - //--------------------------------------------------------------------------------// - // Draw the actual label text - // - font->renderUTF8(mLabel, 0, x, y, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/true); -} - -void LLFolderViewItem::draw() -{ - const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false); - const bool filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : false); // If we have keyboard focus, draw selection filled - - const Params& default_params = LLUICtrlFactory::getDefaultParams(); - const S32 TOP_PAD = default_params.item_top_pad; - - const LLFontGL* font = getLabelFontForStyle(mLabelStyle); - - getViewModelItem()->update(); - - if(!mSingleFolderMode) - { - drawOpenFolderArrow(default_params, sFgColor); - } - - drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); - - //--------------------------------------------------------------------------------// - // Draw open icon - // - const S32 icon_x = mIndentation + mArrowSize + mTextPad; - 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(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); - } - - if (mIconOverlay && getRoot()->showItemLinkOverlays()) - { - mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); - } - - //--------------------------------------------------------------------------------// - // Exit if no label to draw - // - if (mLabel.empty()) - { - return; - } - - std::string::size_type filter_string_length = mViewModelItem->hasFilterStringMatch() ? mViewModelItem->getFilterStringSize() : 0; - F32 right_x = 0; - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; - F32 text_left = (F32)getLabelXPos(); - std::string combined_string = mLabel + mLabelSuffix; - - const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL); - S32 filter_offset = mViewModelItem->getFilterStringOffset(); - if (filter_string_length > 0) - { - S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); - S32 top = getRect().getHeight() - TOP_PAD; - if(mLabelSuffix.empty() || (font == suffix_font)) - { - S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2; - S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2; - - LLUIImage* box_image = default_params.selection_image; - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); - } - else - { - S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); - if(label_filter_length > 0) - { - S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, llmin(filter_offset, (S32)mLabel.size())) - 2; - S32 right = left + font->getWidthF32(mLabel, filter_offset, label_filter_length) + 2; - LLUIImage* box_image = default_params.selection_image; - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); - } - S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; - if(suffix_filter_length > 0) - { - S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); - S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2; - S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2; - LLUIImage* box_image = default_params.selection_image; - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); - } - } - } - - LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor; - - if (isFadeItem()) - { - // Fade out item color to indicate it's being cut - color.mV[VALPHA] *= 0.5f; - } - drawLabel(font, text_left, y, color, right_x); - - //--------------------------------------------------------------------------------// - // Draw label suffix - // - if (!mLabelSuffix.empty()) - { - suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x); - } - - //--------------------------------------------------------------------------------// - // Highlight string match - // - if (filter_string_length > 0) - { - if(mLabelSuffix.empty() || (font == suffix_font)) - { - F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; - font->renderUTF8(combined_string, filter_offset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x); - } - else - { - S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); - if(label_filter_length > 0) - { - F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; - font->renderUTF8(mLabel, filter_offset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - label_filter_length, S32_MAX, &right_x); - } - - S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; - if(suffix_filter_length > 0) - { - S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); - F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length); - F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; - suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - suffix_filter_length, S32_MAX, &right_x); - } - } - - } - - //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to - //be distorted...oddly. I initially added this in but didn't need it after all. So removing to prevent unnecessary bug. - //LLView::draw(); -} - -const LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) const -{ - return getRoot()->getFolderViewModel(); -} - -LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) -{ - return getRoot()->getFolderViewModel(); -} - -bool LLFolderViewItem::isInSelection() const -{ - return mIsSelected || (mParentFolder && mParentFolder->isInSelection()); -} - - - -///---------------------------------------------------------------------------- -/// Class LLFolderViewFolder -///---------------------------------------------------------------------------- - -LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): - LLFolderViewItem( p ), - mIsOpen(false), - mExpanderHighlighted(false), - mCurHeight(0.f), - mTargetHeight(0.f), - mAutoOpenCountdown(0.f), - mIsFolderComplete(false), // folder might have children that are not loaded yet. - mAreChildrenInited(false), // folder might have children that are not built yet. - mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0) -{ -} - -void LLFolderViewFolder::updateLabelRotation() -{ - if (mAutoOpenCountdown != 0.f) - { - mControlLabelRotation = mAutoOpenCountdown * -90.f; - } - else if (isOpen()) - { - mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLSmoothInterpolation::getInterpolant(0.04f)); - } - else - { - mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLSmoothInterpolation::getInterpolant(0.025f)); - } -} - -// Destroys the object -LLFolderViewFolder::~LLFolderViewFolder( void ) -{ - // The LLView base class takes care of object destruction. make sure that we - // don't have mouse or keyboard focus - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() -} - -// addToFolder() returns true if it succeeds. false otherwise -void LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder) -{ - folder->addFolder(this); - - // Compute indentation since parent folder changed - mIndentation = (getParentFolder()) - ? getParentFolder()->getIndentation() + mLocalIndentation - : 0; - - if(isOpen() && folder->isOpen()) - { - requestArrange(); - } -} - -static LLTrace::BlockTimerStatHandle FTM_ARRANGE("Arrange"); - -// Make everything right and in the right place ready for drawing (CHUI-849) -// * Sort everything correctly if necessary -// * Turn widgets visible/invisible according to their model filtering state -// * Takes animation state into account for opening/closing of folders (this makes widgets visible/invisible) -// * Reposition visible widgets so that they line up correctly with no gap -// * Compute the width and height of the current folder and its children -// * Makes sure that this view and its children are the right size -S32 LLFolderViewFolder::arrange( S32* width, S32* height ) -{ - // Sort before laying out contents - // Note that we sort from the root (CHUI-849) - if (mAreChildrenInited) - { - getRoot()->getFolderViewModel()->sort(this); - } - - LL_RECORD_BLOCK_TIME(FTM_ARRANGE); - - // evaluate mHasVisibleChildren - mHasVisibleChildren = false; - if (mAreChildrenInited && getViewModelItem()->descendantsPassedFilter()) - { - // We have to verify that there's at least one child that's not filtered out - bool found = false; - // Try the items first - for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - found = itemp->isPotentiallyVisible(); - if (found) - break; - } - if (!found) - { - // If no item found, try the folders - for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - found = folderp->isPotentiallyVisible(); - if (found) - break; - } - } - - mHasVisibleChildren = found; - } - if (!mIsFolderComplete && mAreChildrenInited) - { - mIsFolderComplete = getFolderViewModel()->isFolderComplete(this); - } - - - - // calculate height as a single item (without any children), and reshapes rectangle to match - LLFolderViewItem::arrange( width, height ); - - // clamp existing animated height so as to never get smaller than a single item - mCurHeight = llmax((F32)*height, mCurHeight); - - // initialize running height value as height of single item in case we have no children - F32 running_height = (F32)*height; - F32 target_height = (F32)*height; - - // are my children visible? - if (needsArrange()) - { - // set last arrange generation first, in case children are animating - // and need to be arranged again - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - if (isOpen()) - { - // Add sizes of children - S32 parent_item_height = getRect().getHeight(); - - for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - folderp->setVisible(folderp->isPotentiallyVisible()); - - if (folderp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - ll_round(running_height); - - target_height += folderp->arrange( &child_width, &child_height ); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); - } - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - itemp->setVisible(itemp->isPotentiallyVisible()); - - if (itemp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - ll_round(running_height); - - target_height += itemp->arrange( &child_width, &child_height ); - // don't change width, as this item is as wide as its parent folder by construction - itemp->reshape( itemp->getRect().getWidth(), child_height); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); - } - } - } - - mTargetHeight = target_height; - // cache this width so next time we can just return it - mLastCalculatedWidth = *width; - } - else - { - // just use existing width - *width = mLastCalculatedWidth; - } - - // animate current height towards target height - if (llabs(mCurHeight - mTargetHeight) > 1.f) - { - mCurHeight = lerp(mCurHeight, mTargetHeight, LLSmoothInterpolation::getInterpolant(isOpen() ? 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();) - { - folders_t::iterator fit = iter++; - // number of pixels that bottom of folder label is from top of parent folder - if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > ll_round(mCurHeight) + mMaxFolderItemOverlap) - { - // hide if beyond current folder height - (*fit)->setVisible(false); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - // number of pixels that bottom of item label is from top of parent folder - if (getRect().getHeight() - (*iit)->getRect().mBottom - > ll_round(mCurHeight) + mMaxFolderItemOverlap) - { - (*iit)->setVisible(false); - } - } - } - else - { - mCurHeight = mTargetHeight; - } - - // don't change width as this item is already as wide as its parent folder - reshape(getRect().getWidth(),ll_round(mCurHeight)); - - // pass current height value back to parent - *height = ll_round(mCurHeight); - - return ll_round(mTargetHeight); -} - -bool LLFolderViewFolder::needsArrange() -{ - return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); -} - -bool LLFolderViewFolder::descendantsPassedFilter(S32 filter_generation) -{ - return getViewModelItem()->descendantsPassedFilter(filter_generation); -} - -// Passes selection information on to children and record selection -// information if necessary. -bool LLFolderViewFolder::setSelection(LLFolderViewItem* selection, bool openitem, - bool take_keyboard_focus) -{ - bool rv = false; - if (selection == this) - { - if (!isSelected()) - { - selectItem(); - } - rv = true; - } - else - { - if (isSelected()) - { - deselectItem(); - } - rv = false; - } - bool child_selected = false; - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if((*fit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = true; - child_selected = true; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = true; - child_selected = true; - } - } - if(openitem && child_selected && !mSingleFolderMode) - { - setOpenArrangeRecursively(true); - } - return rv; -} - -// This method is used to change the selection of an item. -// Recursively traverse all children; if 'selection' is 'this' then change -// the select status if necessary. -// Returns true if the selection state of this folder, or of a child, was changed. -bool LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, bool selected) -{ - bool rv = false; - if(selection == this) - { - if (isSelected() != selected) - { - rv = true; - if (selected) - { - selectItem(); - } - else - { - deselectItem(); - } - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if((*fit)->changeSelection(selection, selected)) - { - rv = true; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->changeSelection(selection, selected)) - { - rv = true; - } - } - return rv; -} - -LLFolderViewFolder* LLFolderViewFolder::getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse) -{ - if (!item_a->getParentFolder() || !item_b->getParentFolder()) return NULL; - - std::deque item_a_ancestors; - - LLFolderViewFolder* parent = item_a->getParentFolder(); - while(parent) - { - item_a_ancestors.push_back(parent); - parent = parent->getParentFolder(); - } - - std::deque item_b_ancestors; - - parent = item_b->getParentFolder(); - while(parent) - { - item_b_ancestors.push_back(parent); - parent = parent->getParentFolder(); - } - - LLFolderViewFolder* common_ancestor = item_a->getRoot(); - - 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(); - } - - return NULL; -} - -void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector& items) -{ - bool selecting = start == NULL; - if (reverse) - { - for (items_t::reverse_iterator it = mItems.rbegin(), end_it = mItems.rend(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - if (selecting && (*it)->getVisible()) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - for (folders_t::reverse_iterator it = mFolders.rbegin(), end_it = mFolders.rend(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting && (*it)->getVisible()) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - } - else - { - for (folders_t::iterator it = mFolders.begin(), end_it = mFolders.end(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting && (*it)->getVisible()) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - for (items_t::iterator it = mItems.begin(), end_it = mItems.end(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting && (*it)->getVisible()) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - } -} - -void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) -{ - if (!getRoot()->getAllowMultiSelect()) - return; - - LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem(); - if (cur_selected_item == NULL) - { - cur_selected_item = new_selection; - } - - - 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) - { - 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(); - } - - std::vector items_to_select_reverse; - - LLFolderViewItem* last_selected_item_from_new = new_selection; - cur_folder = new_selection->getParentFolder(); - while (cur_folder != common_ancestor) - { - 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(); - - bool selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected - - // array always go from 'will be selected' to ' will be unselected', iterate - // in opposite direction to simplify identification of 'point of origin' in - // case it is in the list we are working with - for (std::vector::reverse_iterator it = items_to_select_forward.rbegin(), end_it = items_to_select_forward.rend(); - it != end_it; - ++it) - { - LLFolderViewItem* item = *it; - bool selected = item->isSelected(); - if (!selection_reverse && selected) - { - // it is our 'point of origin' where we shift/expand from - // don't deselect it - selection_reverse = true; - } - else - { - root->changeSelection(item, !selected); - } - } - - if (selection_reverse) - { - // at some point we reversed selection, first element should be deselected - root->changeSelection(last_selected_item_from_cur, false); - } - - // element we expand to should always be selected - root->changeSelection(new_selection, true); -} - - -void LLFolderViewFolder::destroyView() -{ - while (!mItems.empty()) - { - LLFolderViewItem *itemp = mItems.back(); - mItems.pop_back(); - itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems - } - - while (!mFolders.empty()) - { - LLFolderViewFolder *folderp = mFolders.back(); - mFolders.pop_back(); - folderp->destroyView(); // LLFolderVievFolder::destroyView() removes entry from mFolders - } - - LLFolderViewItem::destroyView(); -} - -// extractItem() removes the specified item from the folder, but -// doesn't delete it. -void LLFolderViewFolder::extractItem( LLFolderViewItem* item, bool deparent_model ) -{ - if (item->isSelected()) - getRoot()->clearSelection(); - items_t::iterator it = std::find(mItems.begin(), mItems.end(), item); - if(it == mItems.end()) - { - // This is an evil downcast. However, it's only doing - // pointer comparison to find if (which it should be ) the - // item is in the container, so it's pretty safe. - LLFolderViewFolder* f = static_cast(item); - folders_t::iterator ft; - ft = std::find(mFolders.begin(), mFolders.end(), f); - if (ft != mFolders.end()) - { - mFolders.erase(ft); - } - } - else - { - mItems.erase(it); - } - //item has been removed, need to update filter - if (deparent_model) - { - // in some cases model does not belong to parent view, is shared between views - getViewModelItem()->removeChild(item->getViewModelItem()); - } - //because an item is going away regardless of filter status, force rearrange - requestArrange(); - removeChild(item); -} - -bool LLFolderViewFolder::isMovable() -{ - if( !(getViewModelItem()->isItemMovable()) ) - { - return false; - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if(!(*iit)->isMovable()) - { - return false; - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if(!(*fit)->isMovable()) - { - return false; - } - } - return true; -} - - -bool LLFolderViewFolder::isRemovable() -{ - if( !(getViewModelItem()->isItemRemovable()) ) - { - return false; - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if(!(*iit)->isRemovable()) - { - return false; - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if(!(*fit)->isRemovable()) - { - return false; - } - } - return true; -} - -void LLFolderViewFolder::destroyRoot() -{ - delete this; -} - -// this is an internal method used for adding items to folders. -void LLFolderViewFolder::addItem(LLFolderViewItem* item) -{ - if (item->getParentFolder()) - { - item->getParentFolder()->extractItem(item); - } - item->setParentFolder(this); - - mItems.push_back(item); - - item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); - item->setVisible(false); - - addChild(item); - - // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it - // Note: this happens when models are created before views or shared between views - if (!item->getViewModelItem()->hasParent()) - { - getViewModelItem()->addChild(item->getViewModelItem()); - } -} - -// this is an internal method used for adding items to folders. -void LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) -{ - if (folder->mParentFolder) - { - folder->mParentFolder->extractItem(folder); - } - folder->mParentFolder = this; - mFolders.push_back(folder); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(false); - // rearrange all descendants too, as our indentation level might have changed - //folder->requestArrange(); - //requestSort(); - - addChild(folder); - - // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it - // Note: this happens when models are created before views or shared between views - if (!folder->getViewModelItem()->hasParent()) - { - getViewModelItem()->addChild(folder->getViewModelItem()); - } -} - -void LLFolderViewFolder::requestArrange() -{ - mLastArrangeGeneration = -1; - // flag all items up to root - if (mParentFolder) - { - mParentFolder->requestArrange(); - } -} - -void LLFolderViewFolder::toggleOpen() -{ - setOpen(!isOpen()); -} - -// Force a folder open or closed -void LLFolderViewFolder::setOpen(bool openitem) -{ - if(mSingleFolderMode) - { - // navigateToFolder can destroy this view - // delay it in case setOpen was called from click or key processing - doOnIdleOneTime([this]() - { - getViewModelItem()->navigateToFolder(); - }); - } - else - { - setOpenArrangeRecursively(openitem); - } -} - -void LLFolderViewFolder::setOpenArrangeRecursively(bool openitem, ERecurseType recurse) -{ - bool was_open = isOpen(); - mIsOpen = openitem; - if(!was_open && openitem) - { - getViewModelItem()->openItem(); - // openItem() will request content, it won't be incomplete - mIsFolderComplete = true; - } - else if(was_open && !openitem) - { - getViewModelItem()->closeItem(); - } - - if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */ - } - } - if (mParentFolder - && (recurse == RECURSE_UP - || recurse == RECURSE_UP_DOWN)) - { - mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP); - } - - if (was_open != isOpen()) - { - requestArrange(); - } -} - -bool LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, - bool drop, - EDragAndDropType c_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - bool accepted = mViewModelItem->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); - if (accepted) - { - mDragAndDropTarget = true; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - // drag and drop to child item, so clear pending auto-opens - getRoot()->autoOpenTest(NULL); - - return true; -} - -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();) - { - folders_t::iterator fit = iter++; - (*fit)->applyFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - functor.doItem((*iit)); - } -} - -// LLView functionality -bool LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, - bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - bool handled = false; - - if (isOpen()) - { - handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL); - } - - if (!handled) - { - handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - - LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFolderViewFolder" << LL_ENDL; - } - - return true; -} - -bool LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, - bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - if (!mAllowDrop) - { - *accept = ACCEPT_NO; - tooltip_msg = LLTrans::getString("TooltipOutboxCannotDropOnRoot"); - return true; - } - - bool accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); - - 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 ) -{ - bool handled = false; - - if( isOpen() ) - { - handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - } - if (!handled) - { - handled = LLFolderViewItem::handleRightMouseDown( x, y, mask ); - } - return handled; -} - - -bool LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) -{ - mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); - - bool handled = LLView::handleHover(x, y, mask); - - if (!handled) - { - // this doesn't do child processing - handled = LLFolderViewItem::handleHover(x, y, mask); - } - - return handled; -} - -bool LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - bool handled = false; - if( isOpen() ) - { - handled = childrenHandleMouseDown(x,y,mask) != NULL; - } - if( !handled ) - { - if((mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) - && !mSingleFolderMode) - { - toggleOpen(); - handled = true; - } - else - { - // do normal selection logic - handled = LLFolderViewItem::handleMouseDown(x, y, mask); - } - } - - return handled; -} - -bool LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - bool handled = false; - if(mSingleFolderMode) - { - static LLUICachedControl double_click_new_window("SingleModeDoubleClickOpenWindow", false); - if (double_click_new_window) - { - getViewModelItem()->navigateToFolder(true); - } - else - { - // navigating is going to destroy views and change children - // delay it untill handleDoubleClick processing is complete - doOnIdleOneTime([this]() - { - getViewModelItem()->navigateToFolder(false); - }); - } - return true; - } - - if( isOpen() ) - { - handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - } - if( !handled ) - { - if(mDoubleClickOverride) - { - static LLUICachedControl double_click_action("MultiModeDoubleClickFolder", false); - if (double_click_action == 1) - { - getViewModelItem()->navigateToFolder(true); - return true; - } - if (double_click_action == 2) - { - getViewModelItem()->navigateToFolder(false, true); - return true; - } - } - if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) - { - // don't select when user double-clicks plus sign - // so as not to contradict single-click behavior - toggleOpen(); - } - else - { - getRoot()->setSelection(this, false); - toggleOpen(); - } - handled = true; - } - return handled; -} - -void LLFolderViewFolder::draw() -{ - updateLabelRotation(); - - LLFolderViewItem::draw(); - - // draw children if root folder, or any other folder that is open or animating to closed state - if( getRoot() == this || (isOpen() || mCurHeight != mTargetHeight )) - { - LLView::draw(); - } - - mExpanderHighlighted = false; -} - -// this does prefix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, bool include_children ) -{ - bool found_item = false; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at beginning - if(item == NULL) - { - found_item = true; - } - - // find current item among children - folders_t::iterator fit = mFolders.begin(); - folders_t::iterator fend = mFolders.end(); - - items_t::iterator iit = mItems.begin(); - items_t::iterator iend = mItems.end(); - - // if not trivially starting at the beginning, we have to find the current item - if (!found_item) - { - // first, look among folders, since they are always above items - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = true; - // if we are on downwards traversal - if (include_children && (*fit)->isOpen()) - { - // look for first descendant - return (*fit)->getNextFromChild(NULL, true); - } - // otherwise advance to next folder - ++fit; - include_children = true; - break; - } - } - - // didn't find in folders? Check items... - if (!found_item) - { - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = true; - // point to next item - ++iit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(false); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found folder, continue through folders - // searching for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - // turn on downwards traversal for next folder - ++fit; - } - - if (fit != fend) - { - result = (*fit); - } - else - { - // otherwise, scan for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - // check to see if we have a valid item - if (iit != iend) - { - result = (*iit); - } - } - - if( !result && mParentFolder ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // and skip children for this folder, as we've already discounted them - result = mParentFolder->getNextFromChild(this, false); - } - - return result; -} - -// this does postfix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, bool include_children ) -{ - bool found_item = false; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at end - if(item == NULL) - { - found_item = true; - } - - // find current item among children - folders_t::reverse_iterator fit = mFolders.rbegin(); - folders_t::reverse_iterator fend = mFolders.rend(); - - items_t::reverse_iterator iit = mItems.rbegin(); - items_t::reverse_iterator iend = mItems.rend(); - - // if not trivially starting at the end, we have to find the current item - if (!found_item) - { - // first, look among items, since they are always below the folders - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = true; - // point to next item - ++iit; - break; - } - } - - // didn't find in items? Check folders... - if (!found_item) - { - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = true; - // point to next folder - ++fit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(false); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found item, continue through items - // searching for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - if (iit != iend) - { - // we found an appropriate item - result = (*iit); - } - else - { - // otherwise, scan for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - ++fit; - } - - // check to see if we have a valid folder - if (fit != fend) - { - // try selecting child element of this folder - if ((*fit)->isOpen() && include_children) - { - result = (*fit)->getPreviousFromChild(NULL); - } - else - { - result = (*fit); - } - } - } - - if( !result ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // which gets back to this folder, which will only be visited if it is a valid, visible item - result = this; - } - - return result; -} - +/** +* @file llfolderviewitem.cpp +* @brief Items and folders that can appear in a hierarchical folder view +* +* $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 "../newview/llviewerprecompiledheaders.h" + +#include "llflashtimer.h" + +#include "linden_common.h" +#include "llfolderviewitem.h" +#include "llfolderview.h" +#include "llfolderviewmodel.h" +#include "llpanel.h" +#include "llcallbacklist.h" +#include "llcriticaldamp.h" +#include "llclipboard.h" +#include "llfocusmgr.h" // gFocusMgr +#include "lltrans.h" +#include "llwindow.h" + +///---------------------------------------------------------------------------- +/// Class LLFolderViewItem +///---------------------------------------------------------------------------- + +static LLDefaultChildRegistry::Register r("folder_view_item"); + +// statics +std::map LLFolderViewItem::sFonts; // map of styles to fonts + +bool LLFolderViewItem::sColorSetInitialized = false; +LLUIColor LLFolderViewItem::sFgColor; +LLUIColor LLFolderViewItem::sHighlightBgColor; +LLUIColor LLFolderViewItem::sFlashBgColor; +LLUIColor LLFolderViewItem::sFocusOutlineColor; +LLUIColor LLFolderViewItem::sMouseOverColor; +LLUIColor LLFolderViewItem::sFilterBGColor; +LLUIColor LLFolderViewItem::sFilterTextColor; +LLUIColor LLFolderViewItem::sSuffixColor; +LLUIColor LLFolderViewItem::sSearchStatusColor; + +// 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; + +const LLColor4U DEFAULT_WHITE(255, 255, 255); + + +//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::getFontDefault(); + } + sFonts[style] = rtn; + } + return rtn; +} + +//static +void LLFolderViewItem::initClass() +{ +} + +//static +void LLFolderViewItem::cleanupClass() +{ + sFonts.clear(); +} + + +// NOTE: Optimize this, we call it a *lot* when opening a large inventory +LLFolderViewItem::Params::Params() +: root(), + listener(), + folder_arrow_image("folder_arrow_image"), + folder_indentation("folder_indentation"), + selection_image("selection_image"), + item_height("item_height"), + item_top_pad("item_top_pad"), + creation_date(), + allow_wear("allow_wear", true), + allow_drop("allow_drop", true), + font_color("font_color"), + font_highlight_color("font_highlight_color"), + left_pad("left_pad", 0), + icon_pad("icon_pad", 0), + icon_width("icon_width", 0), + text_pad("text_pad", 0), + text_pad_right("text_pad_right", 0), + single_folder_mode("single_folder_mode", false), + double_click_override("double_click_override", false), + arrow_size("arrow_size", 0), + max_folder_item_overlap("max_folder_item_overlap", 0) +{ } + +// Default constructor +LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) +: LLView(p), + mLabelWidth(0), + mLabelWidthDirty(false), + mSuffixNeedsRefresh(false), + mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT), + mParentFolder( NULL ), + mIsSelected( false ), + mIsCurSelection( false ), + mSelectPending(false), + mIsItemCut(false), + mCutGeneration(0), + mLabelStyle( LLFontGL::NORMAL ), + mHasVisibleChildren(false), + mLocalIndentation(p.folder_indentation), + mIndentation(0), + mItemHeight(p.item_height), + mControlLabelRotation(0.f), + mDragAndDropTarget(false), + mLabel(p.name), + mRoot(p.root), + mViewModelItem(p.listener), + mIsMouseOverTitle(false), + mAllowWear(p.allow_wear), + mAllowDrop(p.allow_drop), + mFontColor(p.font_color), + mFontHighlightColor(p.font_highlight_color), + mLeftPad(p.left_pad), + mIconPad(p.icon_pad), + mIconWidth(p.icon_width), + mTextPad(p.text_pad), + mTextPadRight(p.text_pad_right), + mArrowSize(p.arrow_size), + mSingleFolderMode(p.single_folder_mode), + mMaxFolderItemOverlap(p.max_folder_item_overlap), + mDoubleClickOverride(p.double_click_override) +{ + if (!sColorSetInitialized) + { + sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); + sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE); + sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); + sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); + sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); + sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); + sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); + sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); + sColorSetInitialized = true; + } + + if (mViewModelItem) + { + mViewModelItem->setFolderViewItem(this); + } +} + +// Destroys the object +LLFolderViewItem::~LLFolderViewItem() +{ + mViewModelItem = NULL; + gFocusMgr.removeKeyboardFocusWithoutCallback(this); +} + +bool LLFolderViewItem::postBuild() +{ + LLFolderViewModelItem* vmi = getViewModelItem(); + llassert(vmi); // not supposed to happen, if happens, find out why and fix + if (vmi) + { + // getDisplayName() is expensive (due to internal getLabelSuffix() and name building) + // it also sets search strings so it requires a filter reset + mLabel = vmi->getDisplayName(); + setToolTip(vmi->getName()); + + // Dirty the filter flag of the model from the view (CHUI-849) + vmi->dirtyFilter(); + } + + // Don't do full refresh on constructor if it is possible to avoid + // it significantly slows down bulk view creation. + // Todo: Ideally we need to move getDisplayName() out of constructor as well. + // Like: make a logic that will let filter update search string, + // while LLFolderViewItem::arrange() updates visual part + mSuffixNeedsRefresh = true; + mLabelWidthDirty = true; + return true; +} + +LLFolderView* LLFolderViewItem::getRoot() +{ + return mRoot; +} + +const LLFolderView* LLFolderViewItem::getRoot() const +{ + return mRoot; +} +// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. +bool LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) +{ + LLFolderViewItem* root = this; + while( root->mParentFolder ) + { + if( root->mParentFolder == potential_ancestor ) + { + return true; + } + root = root->mParentFolder; + } + return false; +} + +LLFolderViewItem* LLFolderViewItem::getNextOpenNode(bool include_children) +{ + if (!mParentFolder) + { + return NULL; + } + + LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children ); + while(itemp && !itemp->getVisible()) + { + LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children ); + if (itemp == next_itemp) + { + // hit last item + return itemp->getVisible() ? itemp : this; + } + itemp = next_itemp; + } + + return itemp; +} + +LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(bool include_children) +{ + if (!mParentFolder) + { + return NULL; + } + + LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children ); + + // Skip over items that are invisible or are hidden from the UI. + while(itemp && !itemp->getVisible()) + { + LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children ); + if (itemp == next_itemp) + { + // hit first item + return itemp->getVisible() ? itemp : this; + } + itemp = next_itemp; + } + + return itemp; +} + +bool LLFolderViewItem::passedFilter(S32 filter_generation) +{ + return getViewModelItem()->passedFilter(filter_generation); +} + +bool LLFolderViewItem::isPotentiallyVisible(S32 filter_generation) +{ + if (filter_generation < 0) + { + filter_generation = getFolderViewModel()->getFilter().getFirstSuccessGeneration(); + } + LLFolderViewModelItem* model = getViewModelItem(); + bool visible = model->passedFilter(filter_generation); + if (model->getMarkedDirtyGeneration() >= filter_generation) + { + // unsure visibility state + // retaining previous visibility until item is updated or filter generation changes + visible |= getVisible(); + } + return visible; +} + +void LLFolderViewItem::refresh() +{ + LLFolderViewModelItem& vmi = *getViewModelItem(); + + mLabel = vmi.getDisplayName(); + setToolTip(vmi.getName()); + // icons are slightly expensive to get, can be optimized + // see LLInventoryIcon::getIcon() + mIcon = vmi.getIcon(); + mIconOpen = vmi.getIconOpen(); + mIconOverlay = vmi.getIconOverlay(); + + if (mRoot->useLabelSuffix()) + { + // Very Expensive! + // Can do a number of expensive checks, like checking active motions, wearables or friend list + mLabelStyle = vmi.getLabelStyle(); + mLabelSuffix = vmi.getLabelSuffix(); + } + + // Dirty the filter flag of the model from the view (CHUI-849) + vmi.dirtyFilter(); + + mLabelWidthDirty = true; + mSuffixNeedsRefresh = false; +} + +void LLFolderViewItem::refreshSuffix() +{ + LLFolderViewModelItem const* vmi = getViewModelItem(); + + // icons are slightly expensive to get, can be optimized + // see LLInventoryIcon::getIcon() + mIcon = vmi->getIcon(); + mIconOpen = vmi->getIconOpen(); + mIconOverlay = vmi->getIconOverlay(); + + if (mRoot->useLabelSuffix()) + { + // Very Expensive! + // Can do a number of expensive checks, like checking active motions, wearables or friend list + mLabelStyle = vmi->getLabelStyle(); + mLabelSuffix = vmi->getLabelSuffix(); + } + + mLabelWidthDirty = true; + mSuffixNeedsRefresh = false; +} + +// Utility function for LLFolderView +void LLFolderViewItem::arrangeAndSet(bool set_selection, + bool take_keyboard_focus) +{ + LLFolderView* root = getRoot(); + if (getParentFolder()) + { + getParentFolder()->requestArrange(); + } + if(set_selection) + { + getRoot()->setSelection(this, true, take_keyboard_focus); + if(root) + { + root->scrollToShowSelection(); + } + } +} + + +std::set LLFolderViewItem::getSelectionList() const +{ + std::set selection; + return selection; +} + +// addToFolder() returns true if it succeeds. false otherwise +void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder) +{ + folder->addItem(this); + + // Compute indentation since parent folder changed + mIndentation = (getParentFolder()) + ? getParentFolder()->getIndentation() + mLocalIndentation + : 0; +} + + +// Finds width and height of this object and its children. Also +// makes sure that this view and its children are the right size. +S32 LLFolderViewItem::arrange( S32* width, S32* height ) +{ + // Only indent deeper items in hierarchy + mIndentation = (getParentFolder()) + ? getParentFolder()->getIndentation() + mLocalIndentation + : 0; + if (mLabelWidthDirty) + { + if (mSuffixNeedsRefresh) + { + // Expensive. But despite refreshing label, + // it is purely visual, so it is fine to do at our laisure + refreshSuffix(); + } + mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(LLFontGL::NORMAL)->getWidth(mLabelSuffix) + mLabelPaddingRight; + mLabelWidthDirty = false; + } + + *width = llmax(*width, mLabelWidth); + + // 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() const +{ + return mItemHeight; +} + +S32 LLFolderViewItem::getLabelXPos() +{ + return getIndentation() + mArrowSize + mTextPad + mIconWidth + mIconPad; +} + +S32 LLFolderViewItem::getIconPad() +{ + return mIconPad; +} + +S32 LLFolderViewItem::getTextPad() +{ + return mTextPad; +} + +// *TODO: This can be optimized a lot by simply recording that it is +// selected in the appropriate places, and assuming that set selection +// means 'deselect' for a leaf item. Do this optimization after +// multiple selection is implemented to make sure it all plays nice +// together. +bool LLFolderViewItem::setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus) +{ + if (selection == this && !mIsSelected) + { + selectItem(); + } + else if (mIsSelected) // Deselect everything else. + { + deselectItem(); + } + return mIsSelected; +} + +bool LLFolderViewItem::changeSelection(LLFolderViewItem* selection, bool selected) +{ + if (selection == this) + { + if (mIsSelected) + { + deselectItem(); + } + else + { + selectItem(); + } + return true; + } + return false; +} + +void LLFolderViewItem::deselectItem(void) +{ + mIsSelected = false; +} + +void LLFolderViewItem::selectItem(void) +{ + if (!mIsSelected) + { + mIsSelected = true; + getViewModelItem()->selectItem(); + } +} + +bool LLFolderViewItem::isMovable() +{ + return getViewModelItem()->isItemMovable(); +} + +bool LLFolderViewItem::isRemovable() +{ + return getViewModelItem()->isItemRemovable(); +} + +void LLFolderViewItem::destroyView() +{ + getRoot()->removeFromSelectionList(this); + + if (mParentFolder) + { + // removeView deletes me + mParentFolder->extractItem(this); + } + delete this; +} + +// Call through to the viewed object and return true if it can be +// removed. +//bool LLFolderViewItem::removeRecursively(bool single_item) +bool LLFolderViewItem::remove() +{ + if(!isRemovable()) + { + return false; + } + return getViewModelItem()->removeItem(); +} + +// Build an appropriate context menu for the item. +void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + getViewModelItem()->buildContextMenu(menu, flags); +} + +void LLFolderViewItem::openItem( void ) +{ + if (mAllowWear || !getViewModelItem()->isItemWearable()) + { + getViewModelItem()->openItem(); + } +} + +void LLFolderViewItem::rename(const std::string& new_name) +{ + if( !new_name.empty() ) + { + getViewModelItem()->renameItem(new_name); + } +} + +const std::string& LLFolderViewItem::getName( void ) const +{ + static const std::string noName(""); + return getViewModelItem() ? getViewModelItem()->getName() : noName; +} + +// LLView functionality +bool LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) +{ + if(!mIsSelected) + { + getRoot()->setSelection(this, false); + } + make_ui_sound("UISndClick"); + return true; +} + +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 ); + + if (!mIsSelected) + { + if(mask & MASK_CONTROL) + { + getRoot()->changeSelection(this, !mIsSelected); + } + else if (mask & MASK_SHIFT) + { + getParentFolder()->extendSelectionTo(this); + } + else + { + getRoot()->setSelection(this, false); + } + make_ui_sound("UISndClick"); + } + else + { + // If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. + // This is necessary so we maintain selection consistent when starting a drag. + mSelectPending = true; + } + + mDragStartX = x; + mDragStartY = y; + return true; +} + +bool LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) +{ + mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); + + if( hasMouseCapture() && isMovable() ) + { + LLFolderView* root = getRoot(); + + if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD + && root->getAllowDrag() + && root->getCurSelectedItem() + && root->startDrag()) + { + // RN: when starting drag and drop, clear out last auto-open + root->autoOpenTest(NULL); + root->setShowSelectionContext(true); + + // Release keyboard focus, so that if stuff is dropped into the + // world, pressing the delete key won't blow away the inventory + // item. + gFocusMgr.setKeyboardFocus(NULL); + + getWindow()->setCursor(UI_CURSOR_ARROW); + } + else if (x != mDragStartX || y != mDragStartY) + { + getWindow()->setCursor(UI_CURSOR_NOLOCKED); + } + + root->clearHoveredItem(); + return true; + } + else + { + LLFolderView* pRoot = getRoot(); + pRoot->setHoveredItem(this); + pRoot->setShowSelectionContext(false); + getWindow()->setCursor(UI_CURSOR_ARROW); + // let parent handle this then... + return false; + } +} + + +bool LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) +{ + openItem(); + return true; +} + +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 ) + { + //...then select + if(mask & MASK_CONTROL) + { + getRoot()->changeSelection(this, !mIsSelected); + } + else if (mask & MASK_SHIFT) + { + getParentFolder()->extendSelectionTo(this); + } + else + { + getRoot()->setSelection(this, false); + } + } + + mSelectPending = false; + + if( hasMouseCapture() ) + { + if (getRoot()) + { + getRoot()->setShowSelectionContext(false); + } + gFocusMgr.setMouseCapture( NULL ); + } + return true; +} + +void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + mIsMouseOverTitle = false; + + // NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it + LLFolderView* pRoot = getRoot(); + if (this == pRoot->getHoveredItem()) + { + pRoot->clearHoveredItem(); + } +} + +bool LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + bool handled = false; + bool accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); + handled = accepted; + if (accepted) + { + mDragAndDropTarget = true; + *accept = ACCEPT_YES_MULTI; + } + else + { + *accept = ACCEPT_NO; + } + 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) + { + LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFolderViewItem" << LL_ENDL; + } + + return handled; +} + +void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color) +{ + //--------------------------------------------------------------------------------// + // Draw open folder arrow + // + const S32 TOP_PAD = default_params.item_top_pad; + + if (hasVisibleChildren() || !isFolderComplete()) + { + LLUIImage* arrow_image = default_params.folder_arrow_image; + gl_draw_scaled_rotated_image( + mIndentation, getRect().getHeight() - mArrowSize - mTextPad - TOP_PAD, + mArrowSize, mArrowSize, mControlLabelRotation, arrow_image->getImage(), fg_color); + } +} + +/*virtual*/ bool LLFolderViewItem::isHighlightAllowed() +{ + return mIsSelected; +} + +/*virtual*/ bool LLFolderViewItem::isHighlightActive() +{ + return mIsCurSelection; +} + +/*virtual*/ bool LLFolderViewItem::isFadeItem() +{ + LLClipboard& clipboard = LLClipboard::instance(); + if (mCutGeneration != clipboard.getGeneration()) + { + mCutGeneration = clipboard.getGeneration(); + mIsItemCut = clipboard.isCutMode() + && ((getParentFolder() && getParentFolder()->isFadeItem()) + || getViewModelItem()->isCutToClipboard()); + } + return mIsItemCut; +} + +void LLFolderViewItem::drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, + const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor) +{ + const S32 focus_top = getRect().getHeight(); + const S32 focus_bottom = getRect().getHeight() - mItemHeight; + const bool folder_open = (getRect().getHeight() > mItemHeight + 4); + const S32 FOCUS_LEFT = 1; + + // Determine which background color to use for highlighting + LLUIColor bgColor = (isFlashing() ? flashColor : selectColor); + + //--------------------------------------------------------------------------------// + // Draw highlight for selected items + // Note: Always render "current" item or flashing item, only render other selected + // items if mShowSingleSelection is false. + // + if (isHighlightAllowed()) + + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // Highlight for selected but not current items + if (!isHighlightActive() && !isFlashing()) + { + LLColor4 bg_color = bgColor; + // do time-based fade of extra objects + F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f); + if (getRoot() && getRoot()->getShowSingleSelection()) + { + // fading out + bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); + } + else + { + // fading in + bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); + } + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + bg_color, hasKeyboardFocus); + } + + // Highlight for currently selected or flashing item + if (isHighlightActive()) + { + // Background + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + bgColor, hasKeyboardFocus); + // Outline + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + focusOutlineColor, false); + } + + if (folder_open) + { + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect + getRect().getWidth() - 2, + 0, + focusOutlineColor, false); + if (showContent && !isFlashing()) + { + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, + getRect().getWidth() - 2, + 0, + bgColor, true); + } + } + } + else if (mIsMouseOverTitle) + { + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + mouseOverColor, false); + } + + //--------------------------------------------------------------------------------// + // Draw DragNDrop highlight + // + if (mDragAndDropTarget) + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + bgColor, false); + if (folder_open) + { + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect + getRect().getWidth() - 2, + 0, + bgColor, false); + } + mDragAndDropTarget = false; + } +} + +void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x) +{ + //--------------------------------------------------------------------------------// + // Draw the actual label text + // + font->renderUTF8(mLabel, 0, x, y, color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/true); +} + +void LLFolderViewItem::draw() +{ + const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false); + const bool filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : false); // If we have keyboard focus, draw selection filled + + const Params& default_params = LLUICtrlFactory::getDefaultParams(); + const S32 TOP_PAD = default_params.item_top_pad; + + const LLFontGL* font = getLabelFontForStyle(mLabelStyle); + + getViewModelItem()->update(); + + if(!mSingleFolderMode) + { + drawOpenFolderArrow(default_params, sFgColor); + } + + drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); + + //--------------------------------------------------------------------------------// + // Draw open icon + // + const S32 icon_x = mIndentation + mArrowSize + mTextPad; + 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(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); + } + + if (mIconOverlay && getRoot()->showItemLinkOverlays()) + { + mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); + } + + //--------------------------------------------------------------------------------// + // Exit if no label to draw + // + if (mLabel.empty()) + { + return; + } + + std::string::size_type filter_string_length = mViewModelItem->hasFilterStringMatch() ? mViewModelItem->getFilterStringSize() : 0; + F32 right_x = 0; + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + F32 text_left = (F32)getLabelXPos(); + std::string combined_string = mLabel + mLabelSuffix; + + const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL); + S32 filter_offset = mViewModelItem->getFilterStringOffset(); + if (filter_string_length > 0) + { + S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); + S32 top = getRect().getHeight() - TOP_PAD; + if(mLabelSuffix.empty() || (font == suffix_font)) + { + S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2; + S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2; + + LLUIImage* box_image = default_params.selection_image; + LLRect box_rect(left, top, right, bottom); + box_image->draw(box_rect, sFilterBGColor); + } + else + { + S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); + if(label_filter_length > 0) + { + S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, llmin(filter_offset, (S32)mLabel.size())) - 2; + S32 right = left + font->getWidthF32(mLabel, filter_offset, label_filter_length) + 2; + LLUIImage* box_image = default_params.selection_image; + LLRect box_rect(left, top, right, bottom); + box_image->draw(box_rect, sFilterBGColor); + } + S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; + if(suffix_filter_length > 0) + { + S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); + S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2; + S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2; + LLUIImage* box_image = default_params.selection_image; + LLRect box_rect(left, top, right, bottom); + box_image->draw(box_rect, sFilterBGColor); + } + } + } + + LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor; + + if (isFadeItem()) + { + // Fade out item color to indicate it's being cut + color.mV[VALPHA] *= 0.5f; + } + drawLabel(font, text_left, y, color, right_x); + + //--------------------------------------------------------------------------------// + // Draw label suffix + // + if (!mLabelSuffix.empty()) + { + suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, S32_MAX, &right_x); + } + + //--------------------------------------------------------------------------------// + // Highlight string match + // + if (filter_string_length > 0) + { + if(mLabelSuffix.empty() || (font == suffix_font)) + { + F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length); + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + font->renderUTF8(combined_string, filter_offset, match_string_left, yy, + sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + filter_string_length, S32_MAX, &right_x); + } + else + { + S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); + if(label_filter_length > 0) + { + F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length); + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + font->renderUTF8(mLabel, filter_offset, match_string_left, yy, + sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + label_filter_length, S32_MAX, &right_x); + } + + S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; + if(suffix_filter_length > 0) + { + S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); + F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length); + F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + suffix_filter_length, S32_MAX, &right_x); + } + } + + } + + //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to + //be distorted...oddly. I initially added this in but didn't need it after all. So removing to prevent unnecessary bug. + //LLView::draw(); +} + +const LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) const +{ + return getRoot()->getFolderViewModel(); +} + +LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) +{ + return getRoot()->getFolderViewModel(); +} + +bool LLFolderViewItem::isInSelection() const +{ + return mIsSelected || (mParentFolder && mParentFolder->isInSelection()); +} + + + +///---------------------------------------------------------------------------- +/// Class LLFolderViewFolder +///---------------------------------------------------------------------------- + +LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): + LLFolderViewItem( p ), + mIsOpen(false), + mExpanderHighlighted(false), + mCurHeight(0.f), + mTargetHeight(0.f), + mAutoOpenCountdown(0.f), + mIsFolderComplete(false), // folder might have children that are not loaded yet. + mAreChildrenInited(false), // folder might have children that are not built yet. + mLastArrangeGeneration( -1 ), + mLastCalculatedWidth(0) +{ +} + +void LLFolderViewFolder::updateLabelRotation() +{ + if (mAutoOpenCountdown != 0.f) + { + mControlLabelRotation = mAutoOpenCountdown * -90.f; + } + else if (isOpen()) + { + mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLSmoothInterpolation::getInterpolant(0.04f)); + } + else + { + mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLSmoothInterpolation::getInterpolant(0.025f)); + } +} + +// Destroys the object +LLFolderViewFolder::~LLFolderViewFolder( void ) +{ + // The LLView base class takes care of object destruction. make sure that we + // don't have mouse or keyboard focus + gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() +} + +// addToFolder() returns true if it succeeds. false otherwise +void LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder) +{ + folder->addFolder(this); + + // Compute indentation since parent folder changed + mIndentation = (getParentFolder()) + ? getParentFolder()->getIndentation() + mLocalIndentation + : 0; + + if(isOpen() && folder->isOpen()) + { + requestArrange(); + } +} + +static LLTrace::BlockTimerStatHandle FTM_ARRANGE("Arrange"); + +// Make everything right and in the right place ready for drawing (CHUI-849) +// * Sort everything correctly if necessary +// * Turn widgets visible/invisible according to their model filtering state +// * Takes animation state into account for opening/closing of folders (this makes widgets visible/invisible) +// * Reposition visible widgets so that they line up correctly with no gap +// * Compute the width and height of the current folder and its children +// * Makes sure that this view and its children are the right size +S32 LLFolderViewFolder::arrange( S32* width, S32* height ) +{ + // Sort before laying out contents + // Note that we sort from the root (CHUI-849) + if (mAreChildrenInited) + { + getRoot()->getFolderViewModel()->sort(this); + } + + LL_RECORD_BLOCK_TIME(FTM_ARRANGE); + + // evaluate mHasVisibleChildren + mHasVisibleChildren = false; + if (mAreChildrenInited && getViewModelItem()->descendantsPassedFilter()) + { + // We have to verify that there's at least one child that's not filtered out + bool found = false; + // Try the items first + for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) + { + LLFolderViewItem* itemp = (*iit); + found = itemp->isPotentiallyVisible(); + if (found) + break; + } + if (!found) + { + // If no item found, try the folders + for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) + { + LLFolderViewFolder* folderp = (*fit); + found = folderp->isPotentiallyVisible(); + if (found) + break; + } + } + + mHasVisibleChildren = found; + } + if (!mIsFolderComplete && mAreChildrenInited) + { + mIsFolderComplete = getFolderViewModel()->isFolderComplete(this); + } + + + + // calculate height as a single item (without any children), and reshapes rectangle to match + LLFolderViewItem::arrange( width, height ); + + // clamp existing animated height so as to never get smaller than a single item + mCurHeight = llmax((F32)*height, mCurHeight); + + // initialize running height value as height of single item in case we have no children + F32 running_height = (F32)*height; + F32 target_height = (F32)*height; + + // are my children visible? + if (needsArrange()) + { + // set last arrange generation first, in case children are animating + // and need to be arranged again + mLastArrangeGeneration = getRoot()->getArrangeGeneration(); + if (isOpen()) + { + // Add sizes of children + S32 parent_item_height = getRect().getHeight(); + + for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) + { + LLFolderViewFolder* folderp = (*fit); + folderp->setVisible(folderp->isPotentiallyVisible()); + + if (folderp->getVisible()) + { + S32 child_width = *width; + S32 child_height = 0; + S32 child_top = parent_item_height - ll_round(running_height); + + target_height += folderp->arrange( &child_width, &child_height ); + + running_height += (F32)child_height; + *width = llmax(*width, child_width); + folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); + } + } + for(items_t::iterator iit = mItems.begin(); + iit != mItems.end(); ++iit) + { + LLFolderViewItem* itemp = (*iit); + itemp->setVisible(itemp->isPotentiallyVisible()); + + if (itemp->getVisible()) + { + S32 child_width = *width; + S32 child_height = 0; + S32 child_top = parent_item_height - ll_round(running_height); + + target_height += itemp->arrange( &child_width, &child_height ); + // don't change width, as this item is as wide as its parent folder by construction + itemp->reshape( itemp->getRect().getWidth(), child_height); + + running_height += (F32)child_height; + *width = llmax(*width, child_width); + itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); + } + } + } + + mTargetHeight = target_height; + // cache this width so next time we can just return it + mLastCalculatedWidth = *width; + } + else + { + // just use existing width + *width = mLastCalculatedWidth; + } + + // animate current height towards target height + if (llabs(mCurHeight - mTargetHeight) > 1.f) + { + mCurHeight = lerp(mCurHeight, mTargetHeight, LLSmoothInterpolation::getInterpolant(isOpen() ? 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();) + { + folders_t::iterator fit = iter++; + // number of pixels that bottom of folder label is from top of parent folder + if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() + > ll_round(mCurHeight) + mMaxFolderItemOverlap) + { + // hide if beyond current folder height + (*fit)->setVisible(false); + } + } + + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + // number of pixels that bottom of item label is from top of parent folder + if (getRect().getHeight() - (*iit)->getRect().mBottom + > ll_round(mCurHeight) + mMaxFolderItemOverlap) + { + (*iit)->setVisible(false); + } + } + } + else + { + mCurHeight = mTargetHeight; + } + + // don't change width as this item is already as wide as its parent folder + reshape(getRect().getWidth(),ll_round(mCurHeight)); + + // pass current height value back to parent + *height = ll_round(mCurHeight); + + return ll_round(mTargetHeight); +} + +bool LLFolderViewFolder::needsArrange() +{ + return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); +} + +bool LLFolderViewFolder::descendantsPassedFilter(S32 filter_generation) +{ + return getViewModelItem()->descendantsPassedFilter(filter_generation); +} + +// Passes selection information on to children and record selection +// information if necessary. +bool LLFolderViewFolder::setSelection(LLFolderViewItem* selection, bool openitem, + bool take_keyboard_focus) +{ + bool rv = false; + if (selection == this) + { + if (!isSelected()) + { + selectItem(); + } + rv = true; + } + else + { + if (isSelected()) + { + deselectItem(); + } + rv = false; + } + bool child_selected = false; + + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + if((*fit)->setSelection(selection, openitem, take_keyboard_focus)) + { + rv = true; + child_selected = true; + } + } + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) + { + rv = true; + child_selected = true; + } + } + if(openitem && child_selected && !mSingleFolderMode) + { + setOpenArrangeRecursively(true); + } + return rv; +} + +// This method is used to change the selection of an item. +// Recursively traverse all children; if 'selection' is 'this' then change +// the select status if necessary. +// Returns true if the selection state of this folder, or of a child, was changed. +bool LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, bool selected) +{ + bool rv = false; + if(selection == this) + { + if (isSelected() != selected) + { + rv = true; + if (selected) + { + selectItem(); + } + else + { + deselectItem(); + } + } + } + + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + if((*fit)->changeSelection(selection, selected)) + { + rv = true; + } + } + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + if((*iit)->changeSelection(selection, selected)) + { + rv = true; + } + } + return rv; +} + +LLFolderViewFolder* LLFolderViewFolder::getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse) +{ + if (!item_a->getParentFolder() || !item_b->getParentFolder()) return NULL; + + std::deque item_a_ancestors; + + LLFolderViewFolder* parent = item_a->getParentFolder(); + while(parent) + { + item_a_ancestors.push_back(parent); + parent = parent->getParentFolder(); + } + + std::deque item_b_ancestors; + + parent = item_b->getParentFolder(); + while(parent) + { + item_b_ancestors.push_back(parent); + parent = parent->getParentFolder(); + } + + LLFolderViewFolder* common_ancestor = item_a->getRoot(); + + 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(); + } + + return NULL; +} + +void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector& items) +{ + bool selecting = start == NULL; + if (reverse) + { + for (items_t::reverse_iterator it = mItems.rbegin(), end_it = mItems.rend(); + it != end_it; + ++it) + { + if (*it == end) + { + return; + } + if (selecting && (*it)->getVisible()) + { + items.push_back(*it); + } + + if (*it == start) + { + selecting = true; + } + } + for (folders_t::reverse_iterator it = mFolders.rbegin(), end_it = mFolders.rend(); + it != end_it; + ++it) + { + if (*it == end) + { + return; + } + + if (selecting && (*it)->getVisible()) + { + items.push_back(*it); + } + + if (*it == start) + { + selecting = true; + } + } + } + else + { + for (folders_t::iterator it = mFolders.begin(), end_it = mFolders.end(); + it != end_it; + ++it) + { + if (*it == end) + { + return; + } + + if (selecting && (*it)->getVisible()) + { + items.push_back(*it); + } + + if (*it == start) + { + selecting = true; + } + } + for (items_t::iterator it = mItems.begin(), end_it = mItems.end(); + it != end_it; + ++it) + { + if (*it == end) + { + return; + } + + if (selecting && (*it)->getVisible()) + { + items.push_back(*it); + } + + if (*it == start) + { + selecting = true; + } + } + } +} + +void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) +{ + if (!getRoot()->getAllowMultiSelect()) + return; + + LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem(); + if (cur_selected_item == NULL) + { + cur_selected_item = new_selection; + } + + + 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) + { + 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(); + } + + std::vector items_to_select_reverse; + + LLFolderViewItem* last_selected_item_from_new = new_selection; + cur_folder = new_selection->getParentFolder(); + while (cur_folder != common_ancestor) + { + 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(); + + bool selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected + + // array always go from 'will be selected' to ' will be unselected', iterate + // in opposite direction to simplify identification of 'point of origin' in + // case it is in the list we are working with + for (std::vector::reverse_iterator it = items_to_select_forward.rbegin(), end_it = items_to_select_forward.rend(); + it != end_it; + ++it) + { + LLFolderViewItem* item = *it; + bool selected = item->isSelected(); + if (!selection_reverse && selected) + { + // it is our 'point of origin' where we shift/expand from + // don't deselect it + selection_reverse = true; + } + else + { + root->changeSelection(item, !selected); + } + } + + if (selection_reverse) + { + // at some point we reversed selection, first element should be deselected + root->changeSelection(last_selected_item_from_cur, false); + } + + // element we expand to should always be selected + root->changeSelection(new_selection, true); +} + + +void LLFolderViewFolder::destroyView() +{ + while (!mItems.empty()) + { + LLFolderViewItem *itemp = mItems.back(); + mItems.pop_back(); + itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems + } + + while (!mFolders.empty()) + { + LLFolderViewFolder *folderp = mFolders.back(); + mFolders.pop_back(); + folderp->destroyView(); // LLFolderVievFolder::destroyView() removes entry from mFolders + } + + LLFolderViewItem::destroyView(); +} + +// extractItem() removes the specified item from the folder, but +// doesn't delete it. +void LLFolderViewFolder::extractItem( LLFolderViewItem* item, bool deparent_model ) +{ + if (item->isSelected()) + getRoot()->clearSelection(); + items_t::iterator it = std::find(mItems.begin(), mItems.end(), item); + if(it == mItems.end()) + { + // This is an evil downcast. However, it's only doing + // pointer comparison to find if (which it should be ) the + // item is in the container, so it's pretty safe. + LLFolderViewFolder* f = static_cast(item); + folders_t::iterator ft; + ft = std::find(mFolders.begin(), mFolders.end(), f); + if (ft != mFolders.end()) + { + mFolders.erase(ft); + } + } + else + { + mItems.erase(it); + } + //item has been removed, need to update filter + if (deparent_model) + { + // in some cases model does not belong to parent view, is shared between views + getViewModelItem()->removeChild(item->getViewModelItem()); + } + //because an item is going away regardless of filter status, force rearrange + requestArrange(); + removeChild(item); +} + +bool LLFolderViewFolder::isMovable() +{ + if( !(getViewModelItem()->isItemMovable()) ) + { + return false; + } + + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + if(!(*iit)->isMovable()) + { + return false; + } + } + + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + if(!(*fit)->isMovable()) + { + return false; + } + } + return true; +} + + +bool LLFolderViewFolder::isRemovable() +{ + if( !(getViewModelItem()->isItemRemovable()) ) + { + return false; + } + + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + if(!(*iit)->isRemovable()) + { + return false; + } + } + + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + if(!(*fit)->isRemovable()) + { + return false; + } + } + return true; +} + +void LLFolderViewFolder::destroyRoot() +{ + delete this; +} + +// this is an internal method used for adding items to folders. +void LLFolderViewFolder::addItem(LLFolderViewItem* item) +{ + if (item->getParentFolder()) + { + item->getParentFolder()->extractItem(item); + } + item->setParentFolder(this); + + mItems.push_back(item); + + item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); + item->setVisible(false); + + addChild(item); + + // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it + // Note: this happens when models are created before views or shared between views + if (!item->getViewModelItem()->hasParent()) + { + getViewModelItem()->addChild(item->getViewModelItem()); + } +} + +// this is an internal method used for adding items to folders. +void LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) +{ + if (folder->mParentFolder) + { + folder->mParentFolder->extractItem(folder); + } + folder->mParentFolder = this; + mFolders.push_back(folder); + folder->setOrigin(0, 0); + folder->reshape(getRect().getWidth(), 0); + folder->setVisible(false); + // rearrange all descendants too, as our indentation level might have changed + //folder->requestArrange(); + //requestSort(); + + addChild(folder); + + // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it + // Note: this happens when models are created before views or shared between views + if (!folder->getViewModelItem()->hasParent()) + { + getViewModelItem()->addChild(folder->getViewModelItem()); + } +} + +void LLFolderViewFolder::requestArrange() +{ + mLastArrangeGeneration = -1; + // flag all items up to root + if (mParentFolder) + { + mParentFolder->requestArrange(); + } +} + +void LLFolderViewFolder::toggleOpen() +{ + setOpen(!isOpen()); +} + +// Force a folder open or closed +void LLFolderViewFolder::setOpen(bool openitem) +{ + if(mSingleFolderMode) + { + // navigateToFolder can destroy this view + // delay it in case setOpen was called from click or key processing + doOnIdleOneTime([this]() + { + getViewModelItem()->navigateToFolder(); + }); + } + else + { + setOpenArrangeRecursively(openitem); + } +} + +void LLFolderViewFolder::setOpenArrangeRecursively(bool openitem, ERecurseType recurse) +{ + bool was_open = isOpen(); + mIsOpen = openitem; + if(!was_open && openitem) + { + getViewModelItem()->openItem(); + // openItem() will request content, it won't be incomplete + mIsFolderComplete = true; + } + else if(was_open && !openitem) + { + getViewModelItem()->closeItem(); + } + + if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) + { + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */ + } + } + if (mParentFolder + && (recurse == RECURSE_UP + || recurse == RECURSE_UP_DOWN)) + { + mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP); + } + + if (was_open != isOpen()) + { + requestArrange(); + } +} + +bool LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, + bool drop, + EDragAndDropType c_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + bool accepted = mViewModelItem->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); + if (accepted) + { + mDragAndDropTarget = true; + *accept = ACCEPT_YES_MULTI; + } + else + { + *accept = ACCEPT_NO; + } + + // drag and drop to child item, so clear pending auto-opens + getRoot()->autoOpenTest(NULL); + + return true; +} + +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();) + { + folders_t::iterator fit = iter++; + (*fit)->applyFunctorRecursively(functor); + } + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + functor.doItem((*iit)); + } +} + +// LLView functionality +bool LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + bool handled = false; + + if (isOpen()) + { + handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL); + } + + if (!handled) + { + handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + + LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFolderViewFolder" << LL_ENDL; + } + + return true; +} + +bool LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, + bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + if (!mAllowDrop) + { + *accept = ACCEPT_NO; + tooltip_msg = LLTrans::getString("TooltipOutboxCannotDropOnRoot"); + return true; + } + + bool accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); + + 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 ) +{ + bool handled = false; + + if( isOpen() ) + { + handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; + } + if (!handled) + { + handled = LLFolderViewItem::handleRightMouseDown( x, y, mask ); + } + return handled; +} + + +bool LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) +{ + mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); + + bool handled = LLView::handleHover(x, y, mask); + + if (!handled) + { + // this doesn't do child processing + handled = LLFolderViewItem::handleHover(x, y, mask); + } + + return handled; +} + +bool LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + bool handled = false; + if( isOpen() ) + { + handled = childrenHandleMouseDown(x,y,mask) != NULL; + } + if( !handled ) + { + if((mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) + && !mSingleFolderMode) + { + toggleOpen(); + handled = true; + } + else + { + // do normal selection logic + handled = LLFolderViewItem::handleMouseDown(x, y, mask); + } + } + + return handled; +} + +bool LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) +{ + bool handled = false; + if(mSingleFolderMode) + { + static LLUICachedControl double_click_new_window("SingleModeDoubleClickOpenWindow", false); + if (double_click_new_window) + { + getViewModelItem()->navigateToFolder(true); + } + else + { + // navigating is going to destroy views and change children + // delay it untill handleDoubleClick processing is complete + doOnIdleOneTime([this]() + { + getViewModelItem()->navigateToFolder(false); + }); + } + return true; + } + + if( isOpen() ) + { + handled = childrenHandleDoubleClick( x, y, mask ) != NULL; + } + if( !handled ) + { + if(mDoubleClickOverride) + { + static LLUICachedControl double_click_action("MultiModeDoubleClickFolder", false); + if (double_click_action == 1) + { + getViewModelItem()->navigateToFolder(true); + return true; + } + if (double_click_action == 2) + { + getViewModelItem()->navigateToFolder(false, true); + return true; + } + } + if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) + { + // don't select when user double-clicks plus sign + // so as not to contradict single-click behavior + toggleOpen(); + } + else + { + getRoot()->setSelection(this, false); + toggleOpen(); + } + handled = true; + } + return handled; +} + +void LLFolderViewFolder::draw() +{ + updateLabelRotation(); + + LLFolderViewItem::draw(); + + // draw children if root folder, or any other folder that is open or animating to closed state + if( getRoot() == this || (isOpen() || mCurHeight != mTargetHeight )) + { + LLView::draw(); + } + + mExpanderHighlighted = false; +} + +// this does prefix traversal, as folders are listed above their contents +LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, bool include_children ) +{ + bool found_item = false; + + LLFolderViewItem* result = NULL; + // when not starting from a given item, start at beginning + if(item == NULL) + { + found_item = true; + } + + // find current item among children + folders_t::iterator fit = mFolders.begin(); + folders_t::iterator fend = mFolders.end(); + + items_t::iterator iit = mItems.begin(); + items_t::iterator iend = mItems.end(); + + // if not trivially starting at the beginning, we have to find the current item + if (!found_item) + { + // first, look among folders, since they are always above items + for(; fit != fend; ++fit) + { + if(item == (*fit)) + { + found_item = true; + // if we are on downwards traversal + if (include_children && (*fit)->isOpen()) + { + // look for first descendant + return (*fit)->getNextFromChild(NULL, true); + } + // otherwise advance to next folder + ++fit; + include_children = true; + break; + } + } + + // didn't find in folders? Check items... + if (!found_item) + { + for(; iit != iend; ++iit) + { + if(item == (*iit)) + { + found_item = true; + // point to next item + ++iit; + break; + } + } + } + } + + if (!found_item) + { + // you should never call this method with an item that isn't a child + // so we should always find something + llassert(false); + return NULL; + } + + // at this point, either iit or fit point to a candidate "next" item + // if both are out of range, we need to punt up to our parent + + // now, starting from found folder, continue through folders + // searching for next visible folder + while(fit != fend && !(*fit)->getVisible()) + { + // turn on downwards traversal for next folder + ++fit; + } + + if (fit != fend) + { + result = (*fit); + } + else + { + // otherwise, scan for next visible item + while(iit != iend && !(*iit)->getVisible()) + { + ++iit; + } + + // check to see if we have a valid item + if (iit != iend) + { + result = (*iit); + } + } + + if( !result && mParentFolder ) + { + // If there are no siblings or children to go to, recurse up one level in the tree + // and skip children for this folder, as we've already discounted them + result = mParentFolder->getNextFromChild(this, false); + } + + return result; +} + +// this does postfix traversal, as folders are listed above their contents +LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, bool include_children ) +{ + bool found_item = false; + + LLFolderViewItem* result = NULL; + // when not starting from a given item, start at end + if(item == NULL) + { + found_item = true; + } + + // find current item among children + folders_t::reverse_iterator fit = mFolders.rbegin(); + folders_t::reverse_iterator fend = mFolders.rend(); + + items_t::reverse_iterator iit = mItems.rbegin(); + items_t::reverse_iterator iend = mItems.rend(); + + // if not trivially starting at the end, we have to find the current item + if (!found_item) + { + // first, look among items, since they are always below the folders + for(; iit != iend; ++iit) + { + if(item == (*iit)) + { + found_item = true; + // point to next item + ++iit; + break; + } + } + + // didn't find in items? Check folders... + if (!found_item) + { + for(; fit != fend; ++fit) + { + if(item == (*fit)) + { + found_item = true; + // point to next folder + ++fit; + break; + } + } + } + } + + if (!found_item) + { + // you should never call this method with an item that isn't a child + // so we should always find something + llassert(false); + return NULL; + } + + // at this point, either iit or fit point to a candidate "next" item + // if both are out of range, we need to punt up to our parent + + // now, starting from found item, continue through items + // searching for next visible item + while(iit != iend && !(*iit)->getVisible()) + { + ++iit; + } + + if (iit != iend) + { + // we found an appropriate item + result = (*iit); + } + else + { + // otherwise, scan for next visible folder + while(fit != fend && !(*fit)->getVisible()) + { + ++fit; + } + + // check to see if we have a valid folder + if (fit != fend) + { + // try selecting child element of this folder + if ((*fit)->isOpen() && include_children) + { + result = (*fit)->getPreviousFromChild(NULL); + } + else + { + result = (*fit); + } + } + } + + if( !result ) + { + // If there are no siblings or children to go to, recurse up one level in the tree + // which gets back to this folder, which will only be visited if it is a valid, visible item + result = this; + } + + return result; +} + diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index d72b6804ad..f7ced81274 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -1,498 +1,498 @@ -/** -* @file llfolderviewitem.h -* @brief Items and folders that can appear in a hierarchical folder view -* -* $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$ -*/ -#ifndef LLFOLDERVIEWITEM_H -#define LLFOLDERVIEWITEM_H - -#include "llflashtimer.h" -#include "llview.h" -#include "lluiimage.h" - -class LLFolderView; -class LLFolderViewModelItem; -class LLFolderViewFolder; -class LLFolderViewFunctor; -class LLFolderViewFilter; -class LLFolderViewModelInterface; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewItem -// -// An instance of this class represents a single item in a folder view -// such as an inventory item or a file. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewItem : public LLView -{ -public: - struct Params : public LLInitParam::Block - { - Optional folder_arrow_image, - selection_image; - Mandatory root; - Mandatory listener; - - Optional folder_indentation, // pixels - item_height, - item_top_pad; - - Optional creation_date; - Optional allow_wear; - Optional allow_drop; - - Optional font_color; - Optional font_highlight_color; - - Optional left_pad, - icon_pad, - icon_width, - text_pad, - text_pad_right, - arrow_size, - max_folder_item_overlap; - Optional single_folder_mode, - double_click_override; - Params(); - }; - - - static const S32 DEFAULT_LABEL_PADDING_RIGHT = 4; - // animation parameters - static const F32 FOLDER_CLOSE_TIME_CONSTANT, - FOLDER_OPEN_TIME_CONSTANT; - -protected: - friend class LLUICtrlFactory; - friend class LLFolderViewModelItem; - - LLFolderViewItem(const Params& p); - - std::string mLabel; - S32 mLabelWidth; - bool mLabelWidthDirty; - S32 mLabelPaddingRight; - LLFolderViewFolder* mParentFolder; - LLPointer mViewModelItem; - LLFontGL::StyleFlags mLabelStyle; - std::string mLabelSuffix; - bool mSuffixNeedsRefresh; //suffix and icons - LLUIImagePtr mIcon, - mIconOpen, - mIconOverlay; - S32 mLocalIndentation; - S32 mIndentation; - S32 mItemHeight; - S32 mDragStartX, - mDragStartY; - - S32 mLeftPad, - mIconPad, - mIconWidth, - mTextPad, - mTextPadRight, - mArrowSize, - mMaxFolderItemOverlap; - - F32 mControlLabelRotation; - LLFolderView* mRoot; - bool mHasVisibleChildren, - mIsCurSelection, - mDragAndDropTarget, - mIsMouseOverTitle, - mAllowWear, - mAllowDrop, - mSingleFolderMode, - mDoubleClickOverride, - mSelectPending, - mIsItemCut; - - S32 mCutGeneration; - - LLUIColor mFontColor; - LLUIColor mFontHighlightColor; - - // For now assuming all colors are the same in derived classes. - static bool sColorSetInitialized; - static LLUIColor sFgColor; - static LLUIColor sFgDisabledColor; - static LLUIColor sHighlightBgColor; - static LLUIColor sFlashBgColor; - static LLUIColor sFocusOutlineColor; - static LLUIColor sMouseOverColor; - static LLUIColor sFilterBGColor; - static LLUIColor sFilterTextColor; - static LLUIColor sSuffixColor; - static LLUIColor sSearchStatusColor; - - // this is an internal method used for adding items to folders. A - // no-op at this level, but reimplemented in derived classes. - virtual void addItem(LLFolderViewItem*) { } - virtual void addFolder(LLFolderViewFolder*) { } - virtual bool isHighlightAllowed(); - virtual bool isHighlightActive(); - virtual bool isFadeItem(); - virtual bool isFlashing() { return false; } - virtual void setFlashState(bool) { } - - static LLFontGL* getLabelFontForStyle(U8 style); - - bool mIsSelected; - -public: - static void initClass(); - static void cleanupClass(); - - bool postBuild(); - - virtual void openItem( void ); - - void arrangeAndSet(bool set_selection, bool take_keyboard_focus); - - virtual ~LLFolderViewItem( void ); - - // addToFolder() returns true if it succeeds. false otherwise - virtual void addToFolder(LLFolderViewFolder* folder); - - // 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. - virtual S32 arrange( S32* width, S32* height ); - virtual S32 getItemHeight() const; - virtual S32 getLabelXPos(); - S32 getIconPad(); - S32 getTextPad(); - - // If 'selection' is 'this' then note that otherwise ignore. - // Returns true if this item ends up being selected. - virtual bool setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus); - - // This method is used to set the selection state of an item. - // If 'selection' is 'this' then note selection. - // Returns true if the selection state of this item was changed. - virtual bool changeSelection(LLFolderViewItem* selection, bool selected); - - // this method is used to deselect this element - void deselectItem(); - - // this method is used to select this element - virtual void selectItem(); - - // gets multiple-element selection - virtual std::set getSelectionList() const; - - // Returns true is this object and all of its children can be removed (deleted by user) - virtual bool isRemovable(); - - // Returns true is this object and all of its children can be moved - virtual bool isMovable(); - - // destroys this item recursively - virtual void destroyView(); - - bool isSelected() const { return mIsSelected; } - bool isInSelection() const; - - void setUnselected() { mIsSelected = false; } - - void setIsCurSelection(bool select) { mIsCurSelection = select; } - - bool getIsCurSelection() const { return mIsCurSelection; } - - bool hasVisibleChildren() const { return mHasVisibleChildren; } - - // true if object can't have children - virtual bool isFolderComplete() { return true; } - // true if object can't have children - virtual bool areChildrenInited() { return true; } - virtual void setChildrenInited(bool inited) { } - - // 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); - bool remove(); - - // Build an appropriate context menu for the item. Flags unused. - void buildContextMenu(class LLMenuGL& menu, U32 flags); - - // This method returns the actual name of the thing being - // viewed. This method will ask the viewed object itself. - const std::string& getName( void ) const; - - // This method returns the label displayed on the view. This - // method was primarily added to allow sorting on the folder - // contents possible before the entire view has been constructed. - const std::string& getLabel() const { return mLabel; } - - LLFolderViewFolder* getParentFolder( void ) { return mParentFolder; } - const LLFolderViewFolder* getParentFolder( void ) const { return mParentFolder; } - - void setParentFolder(LLFolderViewFolder* parent) { mParentFolder = parent; } - - LLFolderViewItem* getNextOpenNode( bool include_children = true ); - LLFolderViewItem* getPreviousOpenNode( bool include_children = true ); - - const LLFolderViewModelItem* getViewModelItem( void ) const { return mViewModelItem; } - LLFolderViewModelItem* getViewModelItem( void ) { return mViewModelItem; } - - const LLFolderViewModelInterface* getFolderViewModel( void ) const; - LLFolderViewModelInterface* getFolderViewModel( void ); - - // just rename the object. - void rename(const std::string& new_name); - - // Show children - virtual void setOpen(bool open = true) {}; - virtual bool isOpen() const { return false; } - - virtual LLFolderView* getRoot(); - virtual const LLFolderView* getRoot() const; - bool isDescendantOf( const LLFolderViewFolder* potential_ancestor ); - S32 getIndentation() const { return mIndentation; } - - virtual bool passedFilter(S32 filter_generation = -1); - virtual bool isPotentiallyVisible(S32 filter_generation = -1); - - // refresh information from the object being viewed. - // refreshes label, suffixes and sets icons. Expensive! - // Causes filter update - virtual void refresh(); - // refreshes suffixes and sets icons. Expensive! - // Does not need filter update - virtual void refreshSuffix(); - - bool isSingleFolderMode() { return mSingleFolderMode; } - - // LLView functionality - virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask ); - virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); - virtual bool handleHover( S32 x, S32 y, MASK mask ); - virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); - virtual bool handleDoubleClick( S32 x, S32 y, MASK mask ); - - virtual void onMouseLeave(S32 x, S32 y, MASK mask); - - //virtual LLView* findChildView(const std::string& name, bool recurse) const { return LLView::findChildView(name, recurse); } - - // virtual void handleDropped(); - virtual void draw(); - void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color); - void drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor); - void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); - virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - -private: - static std::map sFonts; // map of styles to fonts -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewFolder -// -// An instance of an LLFolderViewFolder represents a collection of -// more folders and items. This is used to build the hierarchy of -// items found in the folder view. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewFolder : public LLFolderViewItem -{ -protected: - LLFolderViewFolder( const LLFolderViewItem::Params& ); - friend class LLUICtrlFactory; - - void updateLabelRotation(); - virtual bool isCollapsed() { return false; } - -public: - typedef std::list items_t; - typedef std::list folders_t; - -protected: - items_t mItems; - folders_t mFolders; - - bool mIsOpen; - bool mExpanderHighlighted; - F32 mCurHeight; - F32 mTargetHeight; - F32 mAutoOpenCountdown; - S32 mLastArrangeGeneration; - S32 mLastCalculatedWidth; - bool mIsFolderComplete; // indicates that some children were not loaded/added yet - bool mAreChildrenInited; // indicates that no children were initialized - -public: - typedef enum e_recurse_type - { - RECURSE_NO, - RECURSE_UP, - RECURSE_DOWN, - RECURSE_UP_DOWN - } ERecurseType; - - - virtual ~LLFolderViewFolder( void ); - - LLFolderViewItem* getNextFromChild( LLFolderViewItem*, bool include_children = true ); - LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, bool include_children = true ); - - // addToFolder() returns true if it succeeds. false otherwise - virtual void addToFolder(LLFolderViewFolder* folder); - - // 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. - virtual S32 arrange( S32* width, S32* height ); - - bool needsArrange(); - - bool descendantsPassedFilter(S32 filter_generation = -1); - - // Passes selection information on to children and record - // selection information if necessary. - // Returns true if this object (or a child) ends up being selected. - // If 'openitem' is true then folders are opened up along the way to the selection. - virtual bool setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus = true); - - // This method is used to change the selection of an item. - // Recursively traverse all children; if 'selection' is 'this' then change - // the select status if necessary. - // Returns true if the selection state of this folder, or of a child, was changed. - virtual bool changeSelection(LLFolderViewItem* selection, bool selected); - - // this method is used to group select items - void extendSelectionTo(LLFolderViewItem* selection); - - // Returns true is this object and all of its children can be removed. - virtual bool isRemovable(); - - // Returns true is this object and all of its children can be moved - virtual bool isMovable(); - - // destroys this folder, and all children - virtual void destroyView(); - void destroyRoot(); - - // whether known children are fully loaded (arrange sets to true) - virtual bool isFolderComplete() { return mIsFolderComplete; } - - // whether known children are fully built - virtual bool areChildrenInited() { return mAreChildrenInited; } - virtual void setChildrenInited(bool inited) { mAreChildrenInited = inited; } - - // extractItem() removes the specified item from the folder, but - // doesn't delete it. - virtual void extractItem( LLFolderViewItem* item, bool deparent_model = true); - - // This function is called by a child that needs to be resorted. - void resort(LLFolderViewItem* item); - - void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; } - - // folders can be opened. This will usually be called by internal - // methods. - virtual void toggleOpen(); - - // Force a folder open or closed - virtual void setOpen(bool openitem = true); - - // Called when a child is refreshed. - virtual void requestArrange(); - - // internal method which doesn't update the entire view. This - // method was written because the list iterators destroy the state - // of other iterations, thus, we can't arrange while iterating - // through the children (such as when setting which is selected. - virtual void setOpenArrangeRecursively(bool openitem, ERecurseType recurse = RECURSE_NO); - - // Get the current state of the folder. - 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); - - - // Just apply this functor to the folder's immediate children. - void applyFunctorToChildren(LLFolderViewFunctor& functor); - // apply this functor to the folder's descendants. - void applyFunctorRecursively(LLFolderViewFunctor& functor); - - virtual void openItem( void ); - - // LLView functionality - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask ); - 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); - bool handleDragAndDropToThisFolder(MASK mask, - bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - virtual void draw(); - - folders_t::iterator getFoldersBegin() { return mFolders.begin(); } - folders_t::iterator getFoldersEnd() { return mFolders.end(); } - folders_t::size_type getFoldersCount() const { return mFolders.size(); } - - 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); - - // internal functions for tracking folders and items separately - // use addToFolder() virtual method to ensure folders are always added to mFolders - // and not mItems - void addItem(LLFolderViewItem* item); - void addFolder( LLFolderViewFolder* folder); - - //WARNING: do not call directly...use the appropriate LLFolderViewModel-derived class instead - template void sortFolders(const SORT_FUNC& func) { mFolders.sort(func); } - template void sortItems(const SORT_FUNC& func) { mItems.sort(func); } -}; - -typedef std::deque folder_view_item_deque; - -class LLFolderViewGroupedItemModel: public LLRefCount -{ -public: - virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) = 0; -}; - -#endif // LLFOLDERVIEWITEM_H +/** +* @file llfolderviewitem.h +* @brief Items and folders that can appear in a hierarchical folder view +* +* $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$ +*/ +#ifndef LLFOLDERVIEWITEM_H +#define LLFOLDERVIEWITEM_H + +#include "llflashtimer.h" +#include "llview.h" +#include "lluiimage.h" + +class LLFolderView; +class LLFolderViewModelItem; +class LLFolderViewFolder; +class LLFolderViewFunctor; +class LLFolderViewFilter; +class LLFolderViewModelInterface; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFolderViewItem +// +// An instance of this class represents a single item in a folder view +// such as an inventory item or a file. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLFolderViewItem : public LLView +{ +public: + struct Params : public LLInitParam::Block + { + Optional folder_arrow_image, + selection_image; + Mandatory root; + Mandatory listener; + + Optional folder_indentation, // pixels + item_height, + item_top_pad; + + Optional creation_date; + Optional allow_wear; + Optional allow_drop; + + Optional font_color; + Optional font_highlight_color; + + Optional left_pad, + icon_pad, + icon_width, + text_pad, + text_pad_right, + arrow_size, + max_folder_item_overlap; + Optional single_folder_mode, + double_click_override; + Params(); + }; + + + static const S32 DEFAULT_LABEL_PADDING_RIGHT = 4; + // animation parameters + static const F32 FOLDER_CLOSE_TIME_CONSTANT, + FOLDER_OPEN_TIME_CONSTANT; + +protected: + friend class LLUICtrlFactory; + friend class LLFolderViewModelItem; + + LLFolderViewItem(const Params& p); + + std::string mLabel; + S32 mLabelWidth; + bool mLabelWidthDirty; + S32 mLabelPaddingRight; + LLFolderViewFolder* mParentFolder; + LLPointer mViewModelItem; + LLFontGL::StyleFlags mLabelStyle; + std::string mLabelSuffix; + bool mSuffixNeedsRefresh; //suffix and icons + LLUIImagePtr mIcon, + mIconOpen, + mIconOverlay; + S32 mLocalIndentation; + S32 mIndentation; + S32 mItemHeight; + S32 mDragStartX, + mDragStartY; + + S32 mLeftPad, + mIconPad, + mIconWidth, + mTextPad, + mTextPadRight, + mArrowSize, + mMaxFolderItemOverlap; + + F32 mControlLabelRotation; + LLFolderView* mRoot; + bool mHasVisibleChildren, + mIsCurSelection, + mDragAndDropTarget, + mIsMouseOverTitle, + mAllowWear, + mAllowDrop, + mSingleFolderMode, + mDoubleClickOverride, + mSelectPending, + mIsItemCut; + + S32 mCutGeneration; + + LLUIColor mFontColor; + LLUIColor mFontHighlightColor; + + // For now assuming all colors are the same in derived classes. + static bool sColorSetInitialized; + static LLUIColor sFgColor; + static LLUIColor sFgDisabledColor; + static LLUIColor sHighlightBgColor; + static LLUIColor sFlashBgColor; + static LLUIColor sFocusOutlineColor; + static LLUIColor sMouseOverColor; + static LLUIColor sFilterBGColor; + static LLUIColor sFilterTextColor; + static LLUIColor sSuffixColor; + static LLUIColor sSearchStatusColor; + + // this is an internal method used for adding items to folders. A + // no-op at this level, but reimplemented in derived classes. + virtual void addItem(LLFolderViewItem*) { } + virtual void addFolder(LLFolderViewFolder*) { } + virtual bool isHighlightAllowed(); + virtual bool isHighlightActive(); + virtual bool isFadeItem(); + virtual bool isFlashing() { return false; } + virtual void setFlashState(bool) { } + + static LLFontGL* getLabelFontForStyle(U8 style); + + bool mIsSelected; + +public: + static void initClass(); + static void cleanupClass(); + + bool postBuild(); + + virtual void openItem( void ); + + void arrangeAndSet(bool set_selection, bool take_keyboard_focus); + + virtual ~LLFolderViewItem( void ); + + // addToFolder() returns true if it succeeds. false otherwise + virtual void addToFolder(LLFolderViewFolder* folder); + + // 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. + virtual S32 arrange( S32* width, S32* height ); + virtual S32 getItemHeight() const; + virtual S32 getLabelXPos(); + S32 getIconPad(); + S32 getTextPad(); + + // If 'selection' is 'this' then note that otherwise ignore. + // Returns true if this item ends up being selected. + virtual bool setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus); + + // This method is used to set the selection state of an item. + // If 'selection' is 'this' then note selection. + // Returns true if the selection state of this item was changed. + virtual bool changeSelection(LLFolderViewItem* selection, bool selected); + + // this method is used to deselect this element + void deselectItem(); + + // this method is used to select this element + virtual void selectItem(); + + // gets multiple-element selection + virtual std::set getSelectionList() const; + + // Returns true is this object and all of its children can be removed (deleted by user) + virtual bool isRemovable(); + + // Returns true is this object and all of its children can be moved + virtual bool isMovable(); + + // destroys this item recursively + virtual void destroyView(); + + bool isSelected() const { return mIsSelected; } + bool isInSelection() const; + + void setUnselected() { mIsSelected = false; } + + void setIsCurSelection(bool select) { mIsCurSelection = select; } + + bool getIsCurSelection() const { return mIsCurSelection; } + + bool hasVisibleChildren() const { return mHasVisibleChildren; } + + // true if object can't have children + virtual bool isFolderComplete() { return true; } + // true if object can't have children + virtual bool areChildrenInited() { return true; } + virtual void setChildrenInited(bool inited) { } + + // 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); + bool remove(); + + // Build an appropriate context menu for the item. Flags unused. + void buildContextMenu(class LLMenuGL& menu, U32 flags); + + // This method returns the actual name of the thing being + // viewed. This method will ask the viewed object itself. + const std::string& getName( void ) const; + + // This method returns the label displayed on the view. This + // method was primarily added to allow sorting on the folder + // contents possible before the entire view has been constructed. + const std::string& getLabel() const { return mLabel; } + + LLFolderViewFolder* getParentFolder( void ) { return mParentFolder; } + const LLFolderViewFolder* getParentFolder( void ) const { return mParentFolder; } + + void setParentFolder(LLFolderViewFolder* parent) { mParentFolder = parent; } + + LLFolderViewItem* getNextOpenNode( bool include_children = true ); + LLFolderViewItem* getPreviousOpenNode( bool include_children = true ); + + const LLFolderViewModelItem* getViewModelItem( void ) const { return mViewModelItem; } + LLFolderViewModelItem* getViewModelItem( void ) { return mViewModelItem; } + + const LLFolderViewModelInterface* getFolderViewModel( void ) const; + LLFolderViewModelInterface* getFolderViewModel( void ); + + // just rename the object. + void rename(const std::string& new_name); + + // Show children + virtual void setOpen(bool open = true) {}; + virtual bool isOpen() const { return false; } + + virtual LLFolderView* getRoot(); + virtual const LLFolderView* getRoot() const; + bool isDescendantOf( const LLFolderViewFolder* potential_ancestor ); + S32 getIndentation() const { return mIndentation; } + + virtual bool passedFilter(S32 filter_generation = -1); + virtual bool isPotentiallyVisible(S32 filter_generation = -1); + + // refresh information from the object being viewed. + // refreshes label, suffixes and sets icons. Expensive! + // Causes filter update + virtual void refresh(); + // refreshes suffixes and sets icons. Expensive! + // Does not need filter update + virtual void refreshSuffix(); + + bool isSingleFolderMode() { return mSingleFolderMode; } + + // LLView functionality + virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask ); + virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); + virtual bool handleHover( S32 x, S32 y, MASK mask ); + virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); + virtual bool handleDoubleClick( S32 x, S32 y, MASK mask ); + + virtual void onMouseLeave(S32 x, S32 y, MASK mask); + + //virtual LLView* findChildView(const std::string& name, bool recurse) const { return LLView::findChildView(name, recurse); } + + // virtual void handleDropped(); + virtual void draw(); + void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color); + void drawHighlight(const bool showContent, const bool hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor); + void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); + virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + +private: + static std::map sFonts; // map of styles to fonts +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFolderViewFolder +// +// An instance of an LLFolderViewFolder represents a collection of +// more folders and items. This is used to build the hierarchy of +// items found in the folder view. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLFolderViewFolder : public LLFolderViewItem +{ +protected: + LLFolderViewFolder( const LLFolderViewItem::Params& ); + friend class LLUICtrlFactory; + + void updateLabelRotation(); + virtual bool isCollapsed() { return false; } + +public: + typedef std::list items_t; + typedef std::list folders_t; + +protected: + items_t mItems; + folders_t mFolders; + + bool mIsOpen; + bool mExpanderHighlighted; + F32 mCurHeight; + F32 mTargetHeight; + F32 mAutoOpenCountdown; + S32 mLastArrangeGeneration; + S32 mLastCalculatedWidth; + bool mIsFolderComplete; // indicates that some children were not loaded/added yet + bool mAreChildrenInited; // indicates that no children were initialized + +public: + typedef enum e_recurse_type + { + RECURSE_NO, + RECURSE_UP, + RECURSE_DOWN, + RECURSE_UP_DOWN + } ERecurseType; + + + virtual ~LLFolderViewFolder( void ); + + LLFolderViewItem* getNextFromChild( LLFolderViewItem*, bool include_children = true ); + LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, bool include_children = true ); + + // addToFolder() returns true if it succeeds. false otherwise + virtual void addToFolder(LLFolderViewFolder* folder); + + // 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. + virtual S32 arrange( S32* width, S32* height ); + + bool needsArrange(); + + bool descendantsPassedFilter(S32 filter_generation = -1); + + // Passes selection information on to children and record + // selection information if necessary. + // Returns true if this object (or a child) ends up being selected. + // If 'openitem' is true then folders are opened up along the way to the selection. + virtual bool setSelection(LLFolderViewItem* selection, bool openitem, bool take_keyboard_focus = true); + + // This method is used to change the selection of an item. + // Recursively traverse all children; if 'selection' is 'this' then change + // the select status if necessary. + // Returns true if the selection state of this folder, or of a child, was changed. + virtual bool changeSelection(LLFolderViewItem* selection, bool selected); + + // this method is used to group select items + void extendSelectionTo(LLFolderViewItem* selection); + + // Returns true is this object and all of its children can be removed. + virtual bool isRemovable(); + + // Returns true is this object and all of its children can be moved + virtual bool isMovable(); + + // destroys this folder, and all children + virtual void destroyView(); + void destroyRoot(); + + // whether known children are fully loaded (arrange sets to true) + virtual bool isFolderComplete() { return mIsFolderComplete; } + + // whether known children are fully built + virtual bool areChildrenInited() { return mAreChildrenInited; } + virtual void setChildrenInited(bool inited) { mAreChildrenInited = inited; } + + // extractItem() removes the specified item from the folder, but + // doesn't delete it. + virtual void extractItem( LLFolderViewItem* item, bool deparent_model = true); + + // This function is called by a child that needs to be resorted. + void resort(LLFolderViewItem* item); + + void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; } + + // folders can be opened. This will usually be called by internal + // methods. + virtual void toggleOpen(); + + // Force a folder open or closed + virtual void setOpen(bool openitem = true); + + // Called when a child is refreshed. + virtual void requestArrange(); + + // internal method which doesn't update the entire view. This + // method was written because the list iterators destroy the state + // of other iterations, thus, we can't arrange while iterating + // through the children (such as when setting which is selected. + virtual void setOpenArrangeRecursively(bool openitem, ERecurseType recurse = RECURSE_NO); + + // Get the current state of the folder. + 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); + + + // Just apply this functor to the folder's immediate children. + void applyFunctorToChildren(LLFolderViewFunctor& functor); + // apply this functor to the folder's descendants. + void applyFunctorRecursively(LLFolderViewFunctor& functor); + + virtual void openItem( void ); + + // LLView functionality + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask ); + 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); + bool handleDragAndDropToThisFolder(MASK mask, + bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + virtual void draw(); + + folders_t::iterator getFoldersBegin() { return mFolders.begin(); } + folders_t::iterator getFoldersEnd() { return mFolders.end(); } + folders_t::size_type getFoldersCount() const { return mFolders.size(); } + + 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); + + // internal functions for tracking folders and items separately + // use addToFolder() virtual method to ensure folders are always added to mFolders + // and not mItems + void addItem(LLFolderViewItem* item); + void addFolder( LLFolderViewFolder* folder); + + //WARNING: do not call directly...use the appropriate LLFolderViewModel-derived class instead + template void sortFolders(const SORT_FUNC& func) { mFolders.sort(func); } + template void sortItems(const SORT_FUNC& func) { mItems.sort(func); } +}; + +typedef std::deque folder_view_item_deque; + +class LLFolderViewGroupedItemModel: public LLRefCount +{ +public: + virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) = 0; +}; + +#endif // LLFOLDERVIEWITEM_H diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index 27d1f1dbe0..b8d6d89971 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -1,474 +1,474 @@ -/** - * @file llfolderviewmodel.h - * - * $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$ - */ -#ifndef LLFOLDERVIEWMODEL_H -#define LLFOLDERVIEWMODEL_H - -#include "llfontgl.h" // just for StyleFlags enum -#include "llfolderview.h" - -// These are grouping of inventory types. -// Order matters when sorting system folders to the top. -enum EInventorySortGroup -{ - SG_SYSTEM_FOLDER, - SG_TRASH_FOLDER, - SG_NORMAL_FOLDER, - SG_ITEM -}; - -class LLFontGL; -class LLInventoryModel; -class LLMenuGL; -class LLUIImage; -class LLUUID; -class LLFolderViewItem; -class LLFolderViewFolder; - -class LLFolderViewFilter -{ -public: - enum EFilterModified - { - FILTER_NONE, // nothing to do, already filtered - FILTER_RESTART, // restart filtering from scratch - FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter - FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one - }; - -public: - - LLFolderViewFilter() {} - virtual ~LLFolderViewFilter() {} - - // +-------------------------------------------------------------------+ - // + Execution And Results - // +-------------------------------------------------------------------+ - virtual bool check(const LLFolderViewModelItem* item) = 0; - virtual bool checkFolder(const LLFolderViewModelItem* folder) const = 0; - - virtual void setEmptyLookupMessage(const std::string& message) = 0; - virtual std::string getEmptyLookupMessage(bool is_empty_folder = false) const = 0; - - virtual bool showAllResults() const = 0; - - virtual std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const = 0; - virtual std::string::size_type getFilterStringSize() const = 0; - // +-------------------------------------------------------------------+ - // + Status - // +-------------------------------------------------------------------+ - virtual bool isActive() const = 0; - virtual bool isModified() const = 0; - virtual void clearModified() = 0; - virtual const std::string& getName() const = 0; - virtual const std::string& getFilterText() = 0; - //RN: this is public to allow system to externally force a global refilter - virtual void setModified(EFilterModified behavior = FILTER_RESTART) = 0; - - // +-------------------------------------------------------------------+ - // + Time - // +-------------------------------------------------------------------+ - virtual void resetTime(S32 timeout) = 0; - virtual bool isTimedOut() = 0; - - // +-------------------------------------------------------------------+ - // + Default - // +-------------------------------------------------------------------+ - virtual bool isDefault() const = 0; - virtual bool isNotDefault() const = 0; - virtual void markDefault() = 0; - virtual void resetDefault() = 0; - - // +-------------------------------------------------------------------+ - // + Generation - // +-------------------------------------------------------------------+ - virtual S32 getCurrentGeneration() const = 0; - virtual S32 getFirstSuccessGeneration() const = 0; - virtual S32 getFirstRequiredGeneration() const = 0; -}; - -class LLFolderViewModelInterface -{ -public: - LLFolderViewModelInterface() - {} - - virtual ~LLFolderViewModelInterface() {} - virtual void requestSortAll() = 0; - - virtual void sort(class LLFolderViewFolder*) = 0; - virtual void filter() = 0; - - virtual bool contentsReady() = 0; - virtual bool isFolderComplete(class LLFolderViewFolder*) = 0; - virtual void setFolderView(LLFolderView* folder_view) = 0; - virtual LLFolderViewFilter& getFilter() = 0; - virtual const LLFolderViewFilter& getFilter() const = 0; - virtual std::string getStatusText(bool is_empty_folder = false) = 0; - - virtual bool startDrag(std::vector& items) = 0; -}; - -// This is an abstract base class that users of the folderview classes -// would use to bridge the folder view with the underlying data -class LLFolderViewModelItem : public LLRefCount -{ -public: - LLFolderViewModelItem() - {} - - virtual ~LLFolderViewModelItem() { } - - virtual void update() {} //called when drawing - virtual const std::string& getName() const = 0; - virtual const std::string& getDisplayName() const = 0; - virtual const std::string& getSearchableName() const = 0; - - virtual std::string getSearchableDescription() const = 0; - virtual std::string getSearchableCreatorName()const = 0; - virtual std::string getSearchableUUIDString() const = 0; - - virtual LLPointer getIcon() const = 0; - virtual LLPointer getIconOpen() const { return getIcon(); } - virtual LLPointer getIconOverlay() const { return NULL; } - - virtual LLFontGL::StyleFlags getLabelStyle() const = 0; - virtual std::string getLabelSuffix() const = 0; - - virtual void openItem( void ) = 0; - virtual void closeItem( void ) = 0; - virtual void selectItem(void) = 0; - - virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0; - - virtual bool isItemWearable() const { return false; } - - virtual bool isItemRenameable() const = 0; - virtual bool renameItem(const std::string& new_name) = 0; - - virtual bool isItemMovable( void ) const = 0; // Can be moved to another folder - virtual void move( LLFolderViewModelItem* parent_listener ) = 0; - - virtual bool isItemRemovable( bool check_worn = true ) const = 0; // Can be destroyed - virtual bool removeItem() = 0; - virtual void removeBatch(std::vector& batch) = 0; - - virtual bool isItemCopyable(bool can_copy_as_link = true) const = 0; - virtual bool copyToClipboard() const = 0; - virtual bool cutToClipboard() = 0; - virtual bool isCutToClipboard() { return false; }; - - virtual bool isClipboardPasteable() const = 0; - virtual void pasteFromClipboard() = 0; - virtual void pasteLinkFromClipboard() = 0; - - virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; - - virtual bool potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet? - - virtual bool filter( LLFolderViewFilter& filter) = 0; - virtual bool passedFilter(S32 filter_generation = -1) = 0; - virtual bool descendantsPassedFilter(S32 filter_generation = -1) = 0; - virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) = 0; - virtual void setPassedFolderFilter(bool passed, S32 filter_generation) = 0; - virtual void dirtyFilter() = 0; - virtual void dirtyDescendantsFilter() = 0; - virtual bool hasFilterStringMatch() = 0; - virtual std::string::size_type getFilterStringOffset() = 0; - virtual std::string::size_type getFilterStringSize() = 0; - - virtual S32 getLastFilterGeneration() const = 0; - virtual S32 getMarkedDirtyGeneration() const = 0; - - virtual bool hasChildren() const = 0; - virtual void addChild(LLFolderViewModelItem* child) = 0; - virtual void removeChild(LLFolderViewModelItem* child) = 0; - virtual void clearChildren() = 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, - // otherwise false. - virtual bool dragOrDrop(MASK mask, bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - std::string& tooltip_msg) = 0; - - virtual void requestSort() = 0; - virtual S32 getSortVersion() = 0; - virtual void setSortVersion(S32 version) = 0; - virtual void setParent(LLFolderViewModelItem* parent) = 0; - virtual bool hasParent() = 0; - -protected: - - friend class LLFolderViewItem; - virtual void setFolderViewItem(LLFolderViewItem* folder_view_item) = 0; - -}; - - -class LLFolderViewModelItemCommon : public LLFolderViewModelItem -{ -public: - LLFolderViewModelItemCommon(LLFolderViewModelInterface& root_view_model) - : mSortVersion(-1), - mPassedFilter(true), - mPassedFolderFilter(true), - mStringMatchOffsetFilter(std::string::npos), - mStringFilterSize(0), - mFolderViewItem(NULL), - mLastFilterGeneration(-1), - mLastFolderFilterGeneration(-1), - mMarkedDirtyGeneration(-1), - mMostFilteredDescendantGeneration(-1), - mParent(NULL), - mRootViewModel(root_view_model) - { - mChildren.clear(); - } - - void requestSort() { mSortVersion = -1; } - S32 getSortVersion() { return mSortVersion; } - void setSortVersion(S32 version) { mSortVersion = version;} - - S32 getLastFilterGeneration() const { return mLastFilterGeneration; } - S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; } - S32 getMarkedDirtyGeneration() const { return mMarkedDirtyGeneration; } - void dirtyFilter() - { - if(mMarkedDirtyGeneration < 0) - { - mMarkedDirtyGeneration = mLastFilterGeneration; - } - mLastFilterGeneration = -1; - mLastFolderFilterGeneration = -1; - - // bubble up dirty flag all the way to root - if (mParent) - { - mParent->dirtyFilter(); - } - } - void dirtyDescendantsFilter() - { - mMostFilteredDescendantGeneration = -1; - if (mParent) - { - mParent->dirtyDescendantsFilter(); - } - } - bool hasFilterStringMatch(); - std::string::size_type getFilterStringOffset(); - std::string::size_type getFilterStringSize(); - - typedef std::list child_list_t; - - virtual void addChild(LLFolderViewModelItem* child) - { - mChildren.push_back(child); - child->setParent(this); - dirtyFilter(); - requestSort(); - } - virtual void removeChild(LLFolderViewModelItem* child) - { - mChildren.remove(child); - child->setParent(NULL); - dirtyDescendantsFilter(); - dirtyFilter(); - } - - virtual void clearChildren() - { - // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest - std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); }); - mChildren.clear(); - dirtyDescendantsFilter(); - dirtyFilter(); - } - - child_list_t::const_iterator getChildrenBegin() const { return mChildren.begin(); } - child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); } - child_list_t::size_type getChildrenCount() const { return mChildren.size(); } - - void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) - { - mPassedFilter = passed; - mLastFilterGeneration = filter_generation; - mStringMatchOffsetFilter = string_offset; - mStringFilterSize = string_size; - mMarkedDirtyGeneration = -1; - } - - void setPassedFolderFilter(bool passed, S32 filter_generation) - { - mPassedFolderFilter = passed; - mLastFolderFilterGeneration = filter_generation; - } - - virtual bool potentiallyVisible() - { - return passedFilter() // we've passed the filter - || (getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration()) // or we don't know yet - || descendantsPassedFilter(); - } - - virtual bool passedFilter(S32 filter_generation = -1) - { - if (filter_generation < 0) - { - filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration(); - } - bool passed_folder_filter = mPassedFolderFilter && (mLastFolderFilterGeneration >= filter_generation); - bool passed_filter = mPassedFilter && (mLastFilterGeneration >= filter_generation); - return passed_folder_filter && (passed_filter || descendantsPassedFilter(filter_generation)); - } - - virtual bool descendantsPassedFilter(S32 filter_generation = -1) - { - if (filter_generation < 0) - { - filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration(); - } - return mMostFilteredDescendantGeneration >= filter_generation; - } - - -protected: - virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; } - virtual bool hasParent() { return mParent != NULL; } - - S32 mSortVersion; - bool mPassedFilter; - bool mPassedFolderFilter; - std::string::size_type mStringMatchOffsetFilter; - std::string::size_type mStringFilterSize; - - S32 mLastFilterGeneration, - mLastFolderFilterGeneration, - mMostFilteredDescendantGeneration, - mMarkedDirtyGeneration; - - child_list_t mChildren; - LLFolderViewModelItem* mParent; - LLFolderViewModelInterface& mRootViewModel; - - void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;} - LLFolderViewItem* mFolderViewItem; -}; - - - -class LLFolderViewModelCommon : public LLFolderViewModelInterface -{ -public: - LLFolderViewModelCommon() - : mTargetSortVersion(0), - mFolderView(NULL) - {} - - virtual void requestSortAll() - { - // sort everything - mTargetSortVersion++; - } - virtual std::string getStatusText(bool is_empty_folder = false); - virtual void filter(); - - void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} - -protected: - bool needsSort(class LLFolderViewModelItem* item); - - S32 mTargetSortVersion; - LLFolderView* mFolderView; - -}; - -template -class LLFolderViewModel : public LLFolderViewModelCommon -{ -public: - typedef SORT_TYPE SortType; - typedef ITEM_TYPE ItemType; - typedef FOLDER_TYPE FolderType; - typedef FILTER_TYPE FilterType; - - LLFolderViewModel(SortType* sorter, FilterType* filter) - : mSorter(sorter), - mFilter(filter) - {} - - virtual ~LLFolderViewModel() {} - - virtual SortType& getSorter() { return *mSorter; } - virtual const SortType& getSorter() const { return *mSorter; } - virtual void setSorter(const SortType& sorter) { mSorter.reset(new SortType(sorter)); requestSortAll(); } - - virtual FilterType& getFilter() { return *mFilter; } - virtual const FilterType& getFilter() const { return *mFilter; } - virtual void setFilter(const FilterType& filter) { mFilter.reset(new FilterType(filter)); } - - // By default, we assume the content is available. If a network fetch mechanism is implemented for the model, - // this method needs to be overloaded and return the relevant fetch status. - virtual bool contentsReady() { return true; } - virtual bool isFolderComplete(LLFolderViewFolder* folder) { return true; } - - struct ViewModelCompare - { - ViewModelCompare(const SortType& sorter) - : mSorter(sorter) - {} - - bool operator () (const LLFolderViewItem* a, const LLFolderViewItem* b) const - { - return mSorter(static_cast(a->getViewModelItem()), static_cast(b->getViewModelItem())); - } - - bool operator () (const LLFolderViewFolder* a, const LLFolderViewFolder* b) const - { - return mSorter(static_cast(a->getViewModelItem()), static_cast(b->getViewModelItem())); - } - - const SortType& mSorter; - }; - - void sort(LLFolderViewFolder* folder) - { - if (needsSort(folder->getViewModelItem())) - { - folder->sortFolders(ViewModelCompare(getSorter())); - folder->sortItems(ViewModelCompare(getSorter())); - folder->getViewModelItem()->setSortVersion(mTargetSortVersion); - folder->requestArrange(); - } - } - -protected: - std::unique_ptr mSorter; - std::unique_ptr mFilter; -}; - -#endif // LLFOLDERVIEWMODEL_H +/** + * @file llfolderviewmodel.h + * + * $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$ + */ +#ifndef LLFOLDERVIEWMODEL_H +#define LLFOLDERVIEWMODEL_H + +#include "llfontgl.h" // just for StyleFlags enum +#include "llfolderview.h" + +// These are grouping of inventory types. +// Order matters when sorting system folders to the top. +enum EInventorySortGroup +{ + SG_SYSTEM_FOLDER, + SG_TRASH_FOLDER, + SG_NORMAL_FOLDER, + SG_ITEM +}; + +class LLFontGL; +class LLInventoryModel; +class LLMenuGL; +class LLUIImage; +class LLUUID; +class LLFolderViewItem; +class LLFolderViewFolder; + +class LLFolderViewFilter +{ +public: + enum EFilterModified + { + FILTER_NONE, // nothing to do, already filtered + FILTER_RESTART, // restart filtering from scratch + FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter + FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one + }; + +public: + + LLFolderViewFilter() {} + virtual ~LLFolderViewFilter() {} + + // +-------------------------------------------------------------------+ + // + Execution And Results + // +-------------------------------------------------------------------+ + virtual bool check(const LLFolderViewModelItem* item) = 0; + virtual bool checkFolder(const LLFolderViewModelItem* folder) const = 0; + + virtual void setEmptyLookupMessage(const std::string& message) = 0; + virtual std::string getEmptyLookupMessage(bool is_empty_folder = false) const = 0; + + virtual bool showAllResults() const = 0; + + virtual std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const = 0; + virtual std::string::size_type getFilterStringSize() const = 0; + // +-------------------------------------------------------------------+ + // + Status + // +-------------------------------------------------------------------+ + virtual bool isActive() const = 0; + virtual bool isModified() const = 0; + virtual void clearModified() = 0; + virtual const std::string& getName() const = 0; + virtual const std::string& getFilterText() = 0; + //RN: this is public to allow system to externally force a global refilter + virtual void setModified(EFilterModified behavior = FILTER_RESTART) = 0; + + // +-------------------------------------------------------------------+ + // + Time + // +-------------------------------------------------------------------+ + virtual void resetTime(S32 timeout) = 0; + virtual bool isTimedOut() = 0; + + // +-------------------------------------------------------------------+ + // + Default + // +-------------------------------------------------------------------+ + virtual bool isDefault() const = 0; + virtual bool isNotDefault() const = 0; + virtual void markDefault() = 0; + virtual void resetDefault() = 0; + + // +-------------------------------------------------------------------+ + // + Generation + // +-------------------------------------------------------------------+ + virtual S32 getCurrentGeneration() const = 0; + virtual S32 getFirstSuccessGeneration() const = 0; + virtual S32 getFirstRequiredGeneration() const = 0; +}; + +class LLFolderViewModelInterface +{ +public: + LLFolderViewModelInterface() + {} + + virtual ~LLFolderViewModelInterface() {} + virtual void requestSortAll() = 0; + + virtual void sort(class LLFolderViewFolder*) = 0; + virtual void filter() = 0; + + virtual bool contentsReady() = 0; + virtual bool isFolderComplete(class LLFolderViewFolder*) = 0; + virtual void setFolderView(LLFolderView* folder_view) = 0; + virtual LLFolderViewFilter& getFilter() = 0; + virtual const LLFolderViewFilter& getFilter() const = 0; + virtual std::string getStatusText(bool is_empty_folder = false) = 0; + + virtual bool startDrag(std::vector& items) = 0; +}; + +// This is an abstract base class that users of the folderview classes +// would use to bridge the folder view with the underlying data +class LLFolderViewModelItem : public LLRefCount +{ +public: + LLFolderViewModelItem() + {} + + virtual ~LLFolderViewModelItem() { } + + virtual void update() {} //called when drawing + virtual const std::string& getName() const = 0; + virtual const std::string& getDisplayName() const = 0; + virtual const std::string& getSearchableName() const = 0; + + virtual std::string getSearchableDescription() const = 0; + virtual std::string getSearchableCreatorName()const = 0; + virtual std::string getSearchableUUIDString() const = 0; + + virtual LLPointer getIcon() const = 0; + virtual LLPointer getIconOpen() const { return getIcon(); } + virtual LLPointer getIconOverlay() const { return NULL; } + + virtual LLFontGL::StyleFlags getLabelStyle() const = 0; + virtual std::string getLabelSuffix() const = 0; + + virtual void openItem( void ) = 0; + virtual void closeItem( void ) = 0; + virtual void selectItem(void) = 0; + + virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0; + + virtual bool isItemWearable() const { return false; } + + virtual bool isItemRenameable() const = 0; + virtual bool renameItem(const std::string& new_name) = 0; + + virtual bool isItemMovable( void ) const = 0; // Can be moved to another folder + virtual void move( LLFolderViewModelItem* parent_listener ) = 0; + + virtual bool isItemRemovable( bool check_worn = true ) const = 0; // Can be destroyed + virtual bool removeItem() = 0; + virtual void removeBatch(std::vector& batch) = 0; + + virtual bool isItemCopyable(bool can_copy_as_link = true) const = 0; + virtual bool copyToClipboard() const = 0; + virtual bool cutToClipboard() = 0; + virtual bool isCutToClipboard() { return false; }; + + virtual bool isClipboardPasteable() const = 0; + virtual void pasteFromClipboard() = 0; + virtual void pasteLinkFromClipboard() = 0; + + virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; + + virtual bool potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet? + + virtual bool filter( LLFolderViewFilter& filter) = 0; + virtual bool passedFilter(S32 filter_generation = -1) = 0; + virtual bool descendantsPassedFilter(S32 filter_generation = -1) = 0; + virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) = 0; + virtual void setPassedFolderFilter(bool passed, S32 filter_generation) = 0; + virtual void dirtyFilter() = 0; + virtual void dirtyDescendantsFilter() = 0; + virtual bool hasFilterStringMatch() = 0; + virtual std::string::size_type getFilterStringOffset() = 0; + virtual std::string::size_type getFilterStringSize() = 0; + + virtual S32 getLastFilterGeneration() const = 0; + virtual S32 getMarkedDirtyGeneration() const = 0; + + virtual bool hasChildren() const = 0; + virtual void addChild(LLFolderViewModelItem* child) = 0; + virtual void removeChild(LLFolderViewModelItem* child) = 0; + virtual void clearChildren() = 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, + // otherwise false. + virtual bool dragOrDrop(MASK mask, bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) = 0; + + virtual void requestSort() = 0; + virtual S32 getSortVersion() = 0; + virtual void setSortVersion(S32 version) = 0; + virtual void setParent(LLFolderViewModelItem* parent) = 0; + virtual bool hasParent() = 0; + +protected: + + friend class LLFolderViewItem; + virtual void setFolderViewItem(LLFolderViewItem* folder_view_item) = 0; + +}; + + +class LLFolderViewModelItemCommon : public LLFolderViewModelItem +{ +public: + LLFolderViewModelItemCommon(LLFolderViewModelInterface& root_view_model) + : mSortVersion(-1), + mPassedFilter(true), + mPassedFolderFilter(true), + mStringMatchOffsetFilter(std::string::npos), + mStringFilterSize(0), + mFolderViewItem(NULL), + mLastFilterGeneration(-1), + mLastFolderFilterGeneration(-1), + mMarkedDirtyGeneration(-1), + mMostFilteredDescendantGeneration(-1), + mParent(NULL), + mRootViewModel(root_view_model) + { + mChildren.clear(); + } + + void requestSort() { mSortVersion = -1; } + S32 getSortVersion() { return mSortVersion; } + void setSortVersion(S32 version) { mSortVersion = version;} + + S32 getLastFilterGeneration() const { return mLastFilterGeneration; } + S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; } + S32 getMarkedDirtyGeneration() const { return mMarkedDirtyGeneration; } + void dirtyFilter() + { + if(mMarkedDirtyGeneration < 0) + { + mMarkedDirtyGeneration = mLastFilterGeneration; + } + mLastFilterGeneration = -1; + mLastFolderFilterGeneration = -1; + + // bubble up dirty flag all the way to root + if (mParent) + { + mParent->dirtyFilter(); + } + } + void dirtyDescendantsFilter() + { + mMostFilteredDescendantGeneration = -1; + if (mParent) + { + mParent->dirtyDescendantsFilter(); + } + } + bool hasFilterStringMatch(); + std::string::size_type getFilterStringOffset(); + std::string::size_type getFilterStringSize(); + + typedef std::list child_list_t; + + virtual void addChild(LLFolderViewModelItem* child) + { + mChildren.push_back(child); + child->setParent(this); + dirtyFilter(); + requestSort(); + } + virtual void removeChild(LLFolderViewModelItem* child) + { + mChildren.remove(child); + child->setParent(NULL); + dirtyDescendantsFilter(); + dirtyFilter(); + } + + virtual void clearChildren() + { + // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest + std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); }); + mChildren.clear(); + dirtyDescendantsFilter(); + dirtyFilter(); + } + + child_list_t::const_iterator getChildrenBegin() const { return mChildren.begin(); } + child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); } + child_list_t::size_type getChildrenCount() const { return mChildren.size(); } + + void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) + { + mPassedFilter = passed; + mLastFilterGeneration = filter_generation; + mStringMatchOffsetFilter = string_offset; + mStringFilterSize = string_size; + mMarkedDirtyGeneration = -1; + } + + void setPassedFolderFilter(bool passed, S32 filter_generation) + { + mPassedFolderFilter = passed; + mLastFolderFilterGeneration = filter_generation; + } + + virtual bool potentiallyVisible() + { + return passedFilter() // we've passed the filter + || (getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration()) // or we don't know yet + || descendantsPassedFilter(); + } + + virtual bool passedFilter(S32 filter_generation = -1) + { + if (filter_generation < 0) + { + filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration(); + } + bool passed_folder_filter = mPassedFolderFilter && (mLastFolderFilterGeneration >= filter_generation); + bool passed_filter = mPassedFilter && (mLastFilterGeneration >= filter_generation); + return passed_folder_filter && (passed_filter || descendantsPassedFilter(filter_generation)); + } + + virtual bool descendantsPassedFilter(S32 filter_generation = -1) + { + if (filter_generation < 0) + { + filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration(); + } + return mMostFilteredDescendantGeneration >= filter_generation; + } + + +protected: + virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; } + virtual bool hasParent() { return mParent != NULL; } + + S32 mSortVersion; + bool mPassedFilter; + bool mPassedFolderFilter; + std::string::size_type mStringMatchOffsetFilter; + std::string::size_type mStringFilterSize; + + S32 mLastFilterGeneration, + mLastFolderFilterGeneration, + mMostFilteredDescendantGeneration, + mMarkedDirtyGeneration; + + child_list_t mChildren; + LLFolderViewModelItem* mParent; + LLFolderViewModelInterface& mRootViewModel; + + void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;} + LLFolderViewItem* mFolderViewItem; +}; + + + +class LLFolderViewModelCommon : public LLFolderViewModelInterface +{ +public: + LLFolderViewModelCommon() + : mTargetSortVersion(0), + mFolderView(NULL) + {} + + virtual void requestSortAll() + { + // sort everything + mTargetSortVersion++; + } + virtual std::string getStatusText(bool is_empty_folder = false); + virtual void filter(); + + void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} + +protected: + bool needsSort(class LLFolderViewModelItem* item); + + S32 mTargetSortVersion; + LLFolderView* mFolderView; + +}; + +template +class LLFolderViewModel : public LLFolderViewModelCommon +{ +public: + typedef SORT_TYPE SortType; + typedef ITEM_TYPE ItemType; + typedef FOLDER_TYPE FolderType; + typedef FILTER_TYPE FilterType; + + LLFolderViewModel(SortType* sorter, FilterType* filter) + : mSorter(sorter), + mFilter(filter) + {} + + virtual ~LLFolderViewModel() {} + + virtual SortType& getSorter() { return *mSorter; } + virtual const SortType& getSorter() const { return *mSorter; } + virtual void setSorter(const SortType& sorter) { mSorter.reset(new SortType(sorter)); requestSortAll(); } + + virtual FilterType& getFilter() { return *mFilter; } + virtual const FilterType& getFilter() const { return *mFilter; } + virtual void setFilter(const FilterType& filter) { mFilter.reset(new FilterType(filter)); } + + // By default, we assume the content is available. If a network fetch mechanism is implemented for the model, + // this method needs to be overloaded and return the relevant fetch status. + virtual bool contentsReady() { return true; } + virtual bool isFolderComplete(LLFolderViewFolder* folder) { return true; } + + struct ViewModelCompare + { + ViewModelCompare(const SortType& sorter) + : mSorter(sorter) + {} + + bool operator () (const LLFolderViewItem* a, const LLFolderViewItem* b) const + { + return mSorter(static_cast(a->getViewModelItem()), static_cast(b->getViewModelItem())); + } + + bool operator () (const LLFolderViewFolder* a, const LLFolderViewFolder* b) const + { + return mSorter(static_cast(a->getViewModelItem()), static_cast(b->getViewModelItem())); + } + + const SortType& mSorter; + }; + + void sort(LLFolderViewFolder* folder) + { + if (needsSort(folder->getViewModelItem())) + { + folder->sortFolders(ViewModelCompare(getSorter())); + folder->sortItems(ViewModelCompare(getSorter())); + folder->getViewModelItem()->setSortVersion(mTargetSortVersion); + folder->requestArrange(); + } + } + +protected: + std::unique_ptr mSorter; + std::unique_ptr mFilter; +}; + +#endif // LLFOLDERVIEWMODEL_H diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 7492c44aea..83379dd0a9 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -1,173 +1,173 @@ -/** - * @file lliconctrl.cpp - * @brief LLIconCtrl base class - * - * $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 "linden_common.h" - -#include "lliconctrl.h" - -// Linden library includes - -// Project includes -#include "llcontrol.h" -#include "llui.h" -#include "lluictrlfactory.h" -#include "lluiimage.h" -#include "llwindow.h" - -#include "llgltexture.h" - -static LLDefaultChildRegistry::Register r("icon"); - -LLIconCtrl::Params::Params() -: image("image_name"), - color("color"), - use_draw_context_alpha("use_draw_context_alpha", true), - interactable("interactable", false), - scale_image("scale_image"), - min_width("min_width", 0), - min_height("min_height", 0) -{} - -LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p) -: LLUICtrl(p), - mColor(p.color()), - mImagep(p.image), - mUseDrawContextAlpha(p.use_draw_context_alpha), - mInteractable(p.interactable), - mPriority(0), - mMinWidth(p.min_width), - mMinHeight(p.min_height), - mMaxWidth(0), - mMaxHeight(0) -{ - if (mImagep.notNull()) - { - LLUICtrl::setValue(mImagep->getName()); - } -} - -LLIconCtrl::~LLIconCtrl() -{ - mImagep = NULL; -} - - -void LLIconCtrl::draw() -{ - if( mImagep.notNull() ) - { - const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); - mImagep->draw(getLocalRect(), mColor.get() % alpha ); - } - - LLUICtrl::draw(); -} - -bool LLIconCtrl::handleHover(S32 x, S32 y, MASK mask) -{ - if (mInteractable && getEnabled()) - { - getWindow()->setCursor(UI_CURSOR_HAND); - return true; - } - return LLUICtrl::handleHover(x, y, mask); -} - -void LLIconCtrl::onVisibilityChange(bool new_visibility) -{ - LLUICtrl::onVisibilityChange(new_visibility); - if (mPriority == LLGLTexture::BOOST_ICON) - { - if (new_visibility) - { - loadImage(getValue(), mPriority); - } - else - { - mImagep = nullptr; - } - } -} - -// virtual -// value might be a string or a UUID -void LLIconCtrl::setValue(const LLSD& value) -{ - setValue(value, mPriority); -} - -void LLIconCtrl::setValue(const LLSD& value, S32 priority) -{ - LLSD tvalue(value); - if (value.isString() && LLUUID::validate(value.asString())) - { - //RN: support UUIDs masquerading as strings - tvalue = LLSD(LLUUID(value.asString())); - } - LLUICtrl::setValue(tvalue); - - loadImage(tvalue, priority); -} - -void LLIconCtrl::loadImage(const LLSD& tvalue, S32 priority) -{ - if(mPriority == LLGLTexture::BOOST_ICON && !getVisible()) return; - - if (tvalue.isUUID()) - { - mImagep = LLUI::getUIImageByID(tvalue.asUUID(), priority); - } - else - { - mImagep = LLUI::getUIImage(tvalue.asString(), priority); - } - - if(mImagep.notNull() - && mImagep->getImage().notNull() - && mMinWidth - && mMinHeight) - { - S32 desired_draw_width = llmax(mMinWidth, mImagep->getWidth()); - S32 desired_draw_height = llmax(mMinHeight, mImagep->getHeight()); - if (mMaxWidth && mMaxHeight) - { - desired_draw_width = llmin(desired_draw_width, mMaxWidth); - desired_draw_height = llmin(desired_draw_height, mMaxHeight); - } - - mImagep->getImage()->setKnownDrawSize(desired_draw_width, desired_draw_height); - } -} - -std::string LLIconCtrl::getImageName() const -{ - if (getValue().isString()) - return getValue().asString(); - else - return std::string(); -} - - +/** + * @file lliconctrl.cpp + * @brief LLIconCtrl base class + * + * $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 "linden_common.h" + +#include "lliconctrl.h" + +// Linden library includes + +// Project includes +#include "llcontrol.h" +#include "llui.h" +#include "lluictrlfactory.h" +#include "lluiimage.h" +#include "llwindow.h" + +#include "llgltexture.h" + +static LLDefaultChildRegistry::Register r("icon"); + +LLIconCtrl::Params::Params() +: image("image_name"), + color("color"), + use_draw_context_alpha("use_draw_context_alpha", true), + interactable("interactable", false), + scale_image("scale_image"), + min_width("min_width", 0), + min_height("min_height", 0) +{} + +LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p) +: LLUICtrl(p), + mColor(p.color()), + mImagep(p.image), + mUseDrawContextAlpha(p.use_draw_context_alpha), + mInteractable(p.interactable), + mPriority(0), + mMinWidth(p.min_width), + mMinHeight(p.min_height), + mMaxWidth(0), + mMaxHeight(0) +{ + if (mImagep.notNull()) + { + LLUICtrl::setValue(mImagep->getName()); + } +} + +LLIconCtrl::~LLIconCtrl() +{ + mImagep = NULL; +} + + +void LLIconCtrl::draw() +{ + if( mImagep.notNull() ) + { + const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); + mImagep->draw(getLocalRect(), mColor.get() % alpha ); + } + + LLUICtrl::draw(); +} + +bool LLIconCtrl::handleHover(S32 x, S32 y, MASK mask) +{ + if (mInteractable && getEnabled()) + { + getWindow()->setCursor(UI_CURSOR_HAND); + return true; + } + return LLUICtrl::handleHover(x, y, mask); +} + +void LLIconCtrl::onVisibilityChange(bool new_visibility) +{ + LLUICtrl::onVisibilityChange(new_visibility); + if (mPriority == LLGLTexture::BOOST_ICON) + { + if (new_visibility) + { + loadImage(getValue(), mPriority); + } + else + { + mImagep = nullptr; + } + } +} + +// virtual +// value might be a string or a UUID +void LLIconCtrl::setValue(const LLSD& value) +{ + setValue(value, mPriority); +} + +void LLIconCtrl::setValue(const LLSD& value, S32 priority) +{ + LLSD tvalue(value); + if (value.isString() && LLUUID::validate(value.asString())) + { + //RN: support UUIDs masquerading as strings + tvalue = LLSD(LLUUID(value.asString())); + } + LLUICtrl::setValue(tvalue); + + loadImage(tvalue, priority); +} + +void LLIconCtrl::loadImage(const LLSD& tvalue, S32 priority) +{ + if(mPriority == LLGLTexture::BOOST_ICON && !getVisible()) return; + + if (tvalue.isUUID()) + { + mImagep = LLUI::getUIImageByID(tvalue.asUUID(), priority); + } + else + { + mImagep = LLUI::getUIImage(tvalue.asString(), priority); + } + + if(mImagep.notNull() + && mImagep->getImage().notNull() + && mMinWidth + && mMinHeight) + { + S32 desired_draw_width = llmax(mMinWidth, mImagep->getWidth()); + S32 desired_draw_height = llmax(mMinHeight, mImagep->getHeight()); + if (mMaxWidth && mMaxHeight) + { + desired_draw_width = llmin(desired_draw_width, mMaxWidth); + desired_draw_height = llmin(desired_draw_height, mMaxHeight); + } + + mImagep->getImage()->setKnownDrawSize(desired_draw_width, desired_draw_height); + } +} + +std::string LLIconCtrl::getImageName() const +{ + if (getValue().isString()) + return getValue().asString(); + else + return std::string(); +} + + diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index 04910c123c..aae1d1d572 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -1,107 +1,107 @@ -/** - * @file lliconctrl.h - * @brief LLIconCtrl base class - * - * $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$ - */ - -#ifndef LL_LLICONCTRL_H -#define LL_LLICONCTRL_H - -#include "lluuid.h" -#include "v4color.h" -#include "lluictrl.h" -#include "lluiimage.h" - -class LLTextBox; -class LLUICtrlFactory; - -// -// Classes -// - -// Class for diplaying named UI textures -// Do not use for displaying textures from network, -// UI textures are stored permanently! -class LLIconCtrl -: public LLUICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Optional image; - Optional color; - Optional use_draw_context_alpha, - interactable; - Optional min_width, - min_height; - Ignored scale_image; - - Params(); - }; -protected: - LLIconCtrl(const Params&); - friend class LLUICtrlFactory; - - void setValue(const LLSD& value, S32 priority); - -public: - virtual ~LLIconCtrl(); - - // llview overrides - virtual void draw(); - - // llview overrides - virtual bool handleHover(S32 x, S32 y, MASK mask); - - // lluictrl overrides - void onVisibilityChange(bool new_visibility); - virtual void setValue(const LLSD& value ); - - std::string getImageName() const; - - void setColor(const LLColor4& color) { mColor = color; } - void setImage(LLPointer image) { mImagep = image; } - const LLPointer getImage() { return mImagep; } - -protected: - S32 mPriority; - - //the output size of the icon image if set. - S32 mMinWidth, - mMinHeight, - mMaxWidth, - mMaxHeight; - - // If set to true (default), use the draw context transparency. - // If false, will use transparency returned by getCurrentTransparency(). See STORM-698. - bool mUseDrawContextAlpha; - bool mInteractable; - -private: - void loadImage(const LLSD& value, S32 priority); - - LLUIColor mColor; - LLPointer mImagep; -}; - -#endif +/** + * @file lliconctrl.h + * @brief LLIconCtrl base class + * + * $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$ + */ + +#ifndef LL_LLICONCTRL_H +#define LL_LLICONCTRL_H + +#include "lluuid.h" +#include "v4color.h" +#include "lluictrl.h" +#include "lluiimage.h" + +class LLTextBox; +class LLUICtrlFactory; + +// +// Classes +// + +// Class for diplaying named UI textures +// Do not use for displaying textures from network, +// UI textures are stored permanently! +class LLIconCtrl +: public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional image; + Optional color; + Optional use_draw_context_alpha, + interactable; + Optional min_width, + min_height; + Ignored scale_image; + + Params(); + }; +protected: + LLIconCtrl(const Params&); + friend class LLUICtrlFactory; + + void setValue(const LLSD& value, S32 priority); + +public: + virtual ~LLIconCtrl(); + + // llview overrides + virtual void draw(); + + // llview overrides + virtual bool handleHover(S32 x, S32 y, MASK mask); + + // lluictrl overrides + void onVisibilityChange(bool new_visibility); + virtual void setValue(const LLSD& value ); + + std::string getImageName() const; + + void setColor(const LLColor4& color) { mColor = color; } + void setImage(LLPointer image) { mImagep = image; } + const LLPointer getImage() { return mImagep; } + +protected: + S32 mPriority; + + //the output size of the icon image if set. + S32 mMinWidth, + mMinHeight, + mMaxWidth, + mMaxHeight; + + // If set to true (default), use the draw context transparency. + // If false, will use transparency returned by getCurrentTransparency(). See STORM-698. + bool mUseDrawContextAlpha; + bool mInteractable; + +private: + void loadImage(const LLSD& value, S32 priority); + + LLUIColor mColor; + LLPointer mImagep; +}; + +#endif diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 6eeb98fc53..ce644b094c 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -1,813 +1,813 @@ -/** - * @file llkeywords.cpp - * @brief Keyword list for LSL - * - * $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$ - */ - -#include "linden_common.h" - -#include -#include - -#include "llkeywords.h" -#include "llsdserialize.h" -#include "lltexteditor.h" -#include "llstl.h" - -inline bool LLKeywordToken::isHead(const llwchar* s) const -{ - // strncmp is much faster than string compare - bool res = true; - const llwchar* t = mToken.c_str(); - S32 len = mToken.size(); - for (S32 i=0; isecond.get("type").asString() + " " + argsIt->first; - if (argsCount-- > 1) - { - argString += ", "; - } - } - } - else - { - LL_WARNS("SyntaxLSL") << "Argument array comtains a non-map element!" << LL_ENDL; - } - } - } - else if (!arguments.isUndefined()) - { - LL_WARNS("SyntaxLSL") << "Not an array! Invalid arguments LLSD passed to function." << arguments << LL_ENDL; - } - return argString; -} - -std::string LLKeywords::getAttribute(const std::string& key) -{ - attribute_iterator_t it = mAttributes.find(key); - return (it != mAttributes.end()) ? it->second : ""; -} - -LLColor4 LLKeywords::getColorGroup(const std::string& key_in) -{ - std::string color_group = "ScriptText"; - if (key_in == "functions") - { - color_group = "SyntaxLslFunction"; - } - else if (key_in == "controls") - { - color_group = "SyntaxLslControlFlow"; - } - else if (key_in == "events") - { - color_group = "SyntaxLslEvent"; - } - else if (key_in == "types") - { - color_group = "SyntaxLslDataType"; - } - else if (key_in == "misc-flow-label") - { - color_group = "SyntaxLslControlFlow"; - } - else if (key_in =="deprecated") - { - color_group = "SyntaxLslDeprecated"; - } - else if (key_in =="god-mode") - { - color_group = "SyntaxLslGodMode"; - } - else if (key_in == "constants" - || key_in == "constants-integer" - || key_in == "constants-float" - || key_in == "constants-string" - || key_in == "constants-key" - || key_in == "constants-rotation" - || key_in == "constants-vector") - { - color_group = "SyntaxLslConstant"; - } - else - { - LL_WARNS("SyntaxLSL") << "Color key '" << key_in << "' not recognized." << LL_ENDL; - } - - return LLUIColorTable::instance().getColor(color_group); -} - -void LLKeywords::initialize(LLSD SyntaxXML) -{ - mSyntax = SyntaxXML; - mLoaded = true; -} - -void LLKeywords::processTokens() -{ - if (!mLoaded) - { - return; - } - - // Add 'standard' stuff: Quotes, Comments, Strings, Labels, etc. before processing the LLSD - std::string delimiter; - addToken(LLKeywordToken::TT_LABEL, "@", getColorGroup("misc-flow-label"), "Label\nTarget for jump statement", delimiter ); - addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "//", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (single-line)\nNon-functional commentary or disabled code", delimiter ); - addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "/*", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (multi-line)\nNon-functional commentary or disabled code", "*/" ); - addToken(LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS, "\"", LLUIColorTable::instance().getColor("SyntaxLslStringLiteral"), "String literal", "\"" ); - - LLSD::map_iterator itr = mSyntax.beginMap(); - for ( ; itr != mSyntax.endMap(); ++itr) - { - if (itr->first == "llsd-lsl-syntax-version") - { - // Skip over version key. - } - else - { - if (itr->second.isMap()) - { - processTokensGroup(itr->second, itr->first); - } - else - { - LL_WARNS("LSL-Tokens-Processing") << "Map for " + itr->first + " entries is missing! Ignoring." << LL_ENDL; - } - } - } - LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL; -} - -void LLKeywords::processTokensGroup(const LLSD& tokens, const std::string& group) -{ - LLColor4 color; - LLColor4 color_group; - LLColor4 color_deprecated = getColorGroup("deprecated"); - LLColor4 color_god_mode = getColorGroup("god-mode"); - - LLKeywordToken::ETokenType token_type = LLKeywordToken::TT_UNKNOWN; - // If a new token type is added here, it must also be added to the 'addToken' method - if (group == "constants") - { - token_type = LLKeywordToken::TT_CONSTANT; - } - else if (group == "controls") - { - token_type = LLKeywordToken::TT_CONTROL; - } - else if (group == "events") - { - token_type = LLKeywordToken::TT_EVENT; - } - else if (group == "functions") - { - token_type = LLKeywordToken::TT_FUNCTION; - } - else if (group == "label") - { - token_type = LLKeywordToken::TT_LABEL; - } - else if (group == "types") - { - token_type = LLKeywordToken::TT_TYPE; - } - - color_group = getColorGroup(group); - LL_DEBUGS("SyntaxLSL") << "Group: '" << group << "', using color: '" << color_group << "'" << LL_ENDL; - - if (tokens.isMap()) - { - LLSD::map_const_iterator outer_itr = tokens.beginMap(); - for ( ; outer_itr != tokens.endMap(); ++outer_itr ) - { - if (outer_itr->second.isMap()) - { - mAttributes.clear(); - LLSD arguments = LLSD(); - LLSD::map_const_iterator inner_itr = outer_itr->second.beginMap(); - for ( ; inner_itr != outer_itr->second.endMap(); ++inner_itr ) - { - if (inner_itr->first == "arguments") - { - if (inner_itr->second.isArray()) - { - arguments = inner_itr->second; - } - } - else if (!inner_itr->second.isMap() && !inner_itr->second.isArray()) - { - mAttributes[inner_itr->first] = inner_itr->second.asString(); - } - else - { - LL_WARNS("SyntaxLSL") << "Not a valid attribute: " << inner_itr->first << LL_ENDL; - } - } - - std::string tooltip = ""; - switch (token_type) - { - case LLKeywordToken::TT_CONSTANT: - if (getAttribute("type").length() > 0) - { - color_group = getColorGroup(group + "-" + getAttribute("type")); - } - else - { - color_group = getColorGroup(group); - } - tooltip = "Type: " + getAttribute("type") + ", Value: " + getAttribute("value"); - break; - case LLKeywordToken::TT_EVENT: - tooltip = outer_itr->first + "(" + getArguments(arguments) + ")"; - break; - case LLKeywordToken::TT_FUNCTION: - tooltip = getAttribute("return") + " " + outer_itr->first + "(" + getArguments(arguments) + ");"; - tooltip.append("\nEnergy: "); - tooltip.append(getAttribute("energy").empty() ? "0.0" : getAttribute("energy")); - if (!getAttribute("sleep").empty()) - { - tooltip += ", Sleep: " + getAttribute("sleep"); - } - default: - break; - } - - if (!getAttribute("tooltip").empty()) - { - if (!tooltip.empty()) - { - tooltip.append("\n"); - } - tooltip.append(getAttribute("tooltip")); - } - - color = getAttribute("deprecated") == "true" ? color_deprecated : color_group; - - if (getAttribute("god-mode") == "true") - { - color = color_god_mode; - } - - addToken(token_type, outer_itr->first, color, tooltip); - } - } - } - else if (tokens.isArray()) // Currently nothing should need this, but it's here for completeness - { - LL_INFOS("SyntaxLSL") << "Curious, shouldn't be an array here; adding all using color " << color << LL_ENDL; - for (S32 count = 0; count < tokens.size(); ++count) - { - addToken(token_type, tokens[count], color, ""); - } - } - else - { - LL_WARNS("SyntaxLSL") << "Invalid map/array passed: '" << tokens << "'" << LL_ENDL; - } -} - -LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other) -{ - if(other.mOwner) - { - copyData(other.mData, other.mLength); - } - else - { - mOwner = false; - mLength = other.mLength; - mData = other.mData; - } -} - -LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str) -{ - copyData(str.data(), str.size()); -} - -LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length) -: mData(start) -, mLength(length) -, mOwner(false) -{ -} - -LLKeywords::WStringMapIndex::~WStringMapIndex() -{ - if (mOwner) - { - delete[] mData; - } -} - -void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length) -{ - llwchar *data = new llwchar[length]; - memcpy((void*)data, (const void*)start, length * sizeof(llwchar)); - - mOwner = true; - mLength = length; - mData = data; -} - -bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const -{ - // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not. - // The comparison only needs to strictly order all possible strings, and be stable. - - bool result = false; - const llwchar* self_iter = mData; - const llwchar* self_end = mData + mLength; - const llwchar* other_iter = other.mData; - const llwchar* other_end = other.mData + other.mLength; - - while(true) - { - if(other_iter >= other_end) - { - // We've hit the end of other. - // This covers two cases: other being shorter than self, or the strings being equal. - // In either case, we want to return false. - result = false; - break; - } - else if(self_iter >= self_end) - { - // self is shorter than other. - result = true; - break; - } - else if(*self_iter != *other_iter) - { - // The current character differs. The strings are not equal. - result = *self_iter < *other_iter; - break; - } - - self_iter++; - other_iter++; - } - - return result; -} - -LLTrace::BlockTimerStatHandle FTM_SYNTAX_COLORING("Syntax Coloring"); - -// Walk through a string, applying the rules specified by the keyword token list and -// create a list of color segments. -void LLKeywords::findSegments(std::vector* seg_list, const LLWString& wtext, LLTextEditor& editor, LLStyleConstSP style) -{ - LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING); - seg_list->clear(); - - if( wtext.empty() ) - { - return; - } - - S32 text_len = wtext.size() + 1; - - seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); - - const llwchar* base = wtext.c_str(); - const llwchar* cur = base; - while( *cur ) - { - if( *cur == '\n' || cur == base ) - { - if( *cur == '\n' ) - { - LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, cur-base); - text_segment->setToken( 0 ); - insertSegment( *seg_list, text_segment, text_len, style, editor); - cur++; - if( !*cur || *cur == '\n' ) - { - continue; - } - } - - // Skip white space - while( *cur && iswspace(*cur) && (*cur != '\n') ) - { - cur++; - } - if( !*cur || *cur == '\n' ) - { - continue; - } - - // cur is now at the first non-whitespace character of a new line - - // Line start tokens - { - bool line_done = false; - for (token_list_t::iterator iter = mLineTokenList.begin(); - iter != mLineTokenList.end(); ++iter) - { - LLKeywordToken* cur_token = *iter; - if( cur_token->isHead( cur ) ) - { - S32 seg_start = cur - base; - while( *cur && *cur != '\n' ) - { - // skip the rest of the line - cur++; - } - S32 seg_end = cur - base; - - //create segments from seg_start to seg_end - insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor); - line_done = true; // to break out of second loop. - break; - } - } - - if( line_done ) - { - continue; - } - } - } - - // Skip white space - while( *cur && iswspace(*cur) && (*cur != '\n') ) - { - cur++; - } - - while( *cur && *cur != '\n' ) - { - // Check against delimiters - { - S32 seg_start = 0; - LLKeywordToken* cur_delimiter = NULL; - for (token_list_t::iterator iter = mDelimiterTokenList.begin(); - iter != mDelimiterTokenList.end(); ++iter) - { - LLKeywordToken* delimiter = *iter; - if( delimiter->isHead( cur ) ) - { - cur_delimiter = delimiter; - break; - } - } - - if( cur_delimiter ) - { - S32 between_delimiters = 0; - S32 seg_end = 0; - - seg_start = cur - base; - cur += cur_delimiter->getLengthHead(); - - LLKeywordToken::ETokenType type = cur_delimiter->getType(); - if( type == LLKeywordToken::TT_TWO_SIDED_DELIMITER || type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS ) - { - while( *cur && !cur_delimiter->isTail(cur)) - { - // Check for an escape sequence. - if (type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS && *cur == '\\') - { - // Count the number of backslashes. - S32 num_backslashes = 0; - while (*cur == '\\') - { - num_backslashes++; - between_delimiters++; - cur++; - } - // If the next character is the end delimiter? - if (cur_delimiter->isTail(cur)) - { - // If there was an odd number of backslashes, then this delimiter - // does not end the sequence. - if (num_backslashes % 2 == 1) - { - between_delimiters++; - cur++; - } - else - { - // This is an end delimiter. - break; - } - } - } - else - { - between_delimiters++; - cur++; - } - } - - if( *cur ) - { - cur += cur_delimiter->getLengthHead(); - seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead() + cur_delimiter->getLengthTail(); - } - else - { - // eof - seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead(); - } - } - else - { - llassert( cur_delimiter->getType() == LLKeywordToken::TT_ONE_SIDED_DELIMITER ); - // Left side is the delimiter. Right side is eol or eof. - while( *cur && ('\n' != *cur) ) - { - between_delimiters++; - cur++; - } - seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead(); - } - - insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, style, editor); - /* - LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor ); - text_segment->setToken( cur_delimiter ); - insertSegment( seg_list, text_segment, text_len, defaultColor, editor); - */ - // Note: we don't increment cur, since the end of one delimited seg may be immediately - // followed by the start of another one. - continue; - } - } - - // check against words - llwchar prev = cur > base ? *(cur-1) : 0; - if( !iswalnum( prev ) && (prev != '_') ) - { - const llwchar* p = cur; - while( iswalnum( *p ) || (*p == '_') ) - { - p++; - } - S32 seg_len = p - cur; - if( seg_len > 0 ) - { - WStringMapIndex word( cur, seg_len ); - word_token_map_t::iterator map_iter = mWordTokenMap.find(word); - if( map_iter != mWordTokenMap.end() ) - { - LLKeywordToken* cur_token = map_iter->second; - S32 seg_start = cur - base; - S32 seg_end = seg_start + seg_len; - - // LL_INFOS("SyntaxLSL") << "Seg: [" << word.c_str() << "]" << LL_ENDL; - - insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor); - } - cur += seg_len; - continue; - } - } - - if( *cur && *cur != '\n' ) - { - cur++; - } - } - } -} - -void LLKeywords::insertSegments(const LLWString& wtext, std::vector& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, LLStyleConstSP style, LLTextEditor& editor ) -{ - std::string::size_type pos = wtext.find('\n',seg_start); - - LLStyleConstSP cur_token_style = new LLStyle(LLStyle::Params().font(style->getFont()).color(cur_token->getColor())); - - while (pos!=-1 && pos < (std::string::size_type)seg_end) - { - if (pos!=seg_start) - { - LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, pos, editor); - text_segment->setToken( cur_token ); - insertSegment( seg_list, text_segment, text_len, style, editor); - } - - LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, pos); - text_segment->setToken( cur_token ); - insertSegment( seg_list, text_segment, text_len, style, editor); - - seg_start = pos+1; - pos = wtext.find('\n',seg_start); - } - - LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, seg_end, editor); - text_segment->setToken( cur_token ); - insertSegment( seg_list, text_segment, text_len, style, editor); -} - -void LLKeywords::insertSegment(std::vector& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor ) -{ - LLTextSegmentPtr last = seg_list.back(); - S32 new_seg_end = new_segment->getEnd(); - - if( new_segment->getStart() == last->getStart() ) - { - seg_list.pop_back(); - } - else - { - last->setEnd( new_segment->getStart() ); - } - seg_list.push_back( new_segment ); - - if( new_seg_end < text_len ) - { - seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) ); - } -} - -void LLKeywords::insertSegment(std::vector& seg_list, LLTextSegmentPtr new_segment, S32 text_len, LLStyleConstSP style, LLTextEditor& editor ) -{ - LLTextSegmentPtr last = seg_list.back(); - S32 new_seg_end = new_segment->getEnd(); - - if( new_segment->getStart() == last->getStart() ) - { - seg_list.pop_back(); - } - else - { - last->setEnd( new_segment->getStart() ); - } - seg_list.push_back( new_segment ); - - if( new_seg_end < text_len ) - { - seg_list.push_back( new LLNormalTextSegment( style, new_seg_end, text_len, editor ) ); - } -} - -#ifdef _DEBUG -void LLKeywords::dump() -{ - LL_INFOS() << "LLKeywords" << LL_ENDL; - - - LL_INFOS() << "LLKeywords::sWordTokenMap" << LL_ENDL; - word_token_map_t::iterator word_token_iter = mWordTokenMap.begin(); - while( word_token_iter != mWordTokenMap.end() ) - { - LLKeywordToken* word_token = word_token_iter->second; - word_token->dump(); - ++word_token_iter; - } - - LL_INFOS() << "LLKeywords::sLineTokenList" << LL_ENDL; - for (token_list_t::iterator iter = mLineTokenList.begin(); - iter != mLineTokenList.end(); ++iter) - { - LLKeywordToken* line_token = *iter; - line_token->dump(); - } - - - LL_INFOS() << "LLKeywords::sDelimiterTokenList" << LL_ENDL; - for (token_list_t::iterator iter = mDelimiterTokenList.begin(); - iter != mDelimiterTokenList.end(); ++iter) - { - LLKeywordToken* delimiter_token = *iter; - delimiter_token->dump(); - } -} - -void LLKeywordToken::dump() -{ - LL_INFOS() << "[" << - mColor.mV[VX] << ", " << - mColor.mV[VY] << ", " << - mColor.mV[VZ] << "] [" << - wstring_to_utf8str(mToken) << "]" << - LL_ENDL; -} - -#endif // DEBUG +/** + * @file llkeywords.cpp + * @brief Keyword list for LSL + * + * $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$ + */ + +#include "linden_common.h" + +#include +#include + +#include "llkeywords.h" +#include "llsdserialize.h" +#include "lltexteditor.h" +#include "llstl.h" + +inline bool LLKeywordToken::isHead(const llwchar* s) const +{ + // strncmp is much faster than string compare + bool res = true; + const llwchar* t = mToken.c_str(); + S32 len = mToken.size(); + for (S32 i=0; isecond.get("type").asString() + " " + argsIt->first; + if (argsCount-- > 1) + { + argString += ", "; + } + } + } + else + { + LL_WARNS("SyntaxLSL") << "Argument array comtains a non-map element!" << LL_ENDL; + } + } + } + else if (!arguments.isUndefined()) + { + LL_WARNS("SyntaxLSL") << "Not an array! Invalid arguments LLSD passed to function." << arguments << LL_ENDL; + } + return argString; +} + +std::string LLKeywords::getAttribute(const std::string& key) +{ + attribute_iterator_t it = mAttributes.find(key); + return (it != mAttributes.end()) ? it->second : ""; +} + +LLColor4 LLKeywords::getColorGroup(const std::string& key_in) +{ + std::string color_group = "ScriptText"; + if (key_in == "functions") + { + color_group = "SyntaxLslFunction"; + } + else if (key_in == "controls") + { + color_group = "SyntaxLslControlFlow"; + } + else if (key_in == "events") + { + color_group = "SyntaxLslEvent"; + } + else if (key_in == "types") + { + color_group = "SyntaxLslDataType"; + } + else if (key_in == "misc-flow-label") + { + color_group = "SyntaxLslControlFlow"; + } + else if (key_in =="deprecated") + { + color_group = "SyntaxLslDeprecated"; + } + else if (key_in =="god-mode") + { + color_group = "SyntaxLslGodMode"; + } + else if (key_in == "constants" + || key_in == "constants-integer" + || key_in == "constants-float" + || key_in == "constants-string" + || key_in == "constants-key" + || key_in == "constants-rotation" + || key_in == "constants-vector") + { + color_group = "SyntaxLslConstant"; + } + else + { + LL_WARNS("SyntaxLSL") << "Color key '" << key_in << "' not recognized." << LL_ENDL; + } + + return LLUIColorTable::instance().getColor(color_group); +} + +void LLKeywords::initialize(LLSD SyntaxXML) +{ + mSyntax = SyntaxXML; + mLoaded = true; +} + +void LLKeywords::processTokens() +{ + if (!mLoaded) + { + return; + } + + // Add 'standard' stuff: Quotes, Comments, Strings, Labels, etc. before processing the LLSD + std::string delimiter; + addToken(LLKeywordToken::TT_LABEL, "@", getColorGroup("misc-flow-label"), "Label\nTarget for jump statement", delimiter ); + addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "//", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (single-line)\nNon-functional commentary or disabled code", delimiter ); + addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "/*", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (multi-line)\nNon-functional commentary or disabled code", "*/" ); + addToken(LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS, "\"", LLUIColorTable::instance().getColor("SyntaxLslStringLiteral"), "String literal", "\"" ); + + LLSD::map_iterator itr = mSyntax.beginMap(); + for ( ; itr != mSyntax.endMap(); ++itr) + { + if (itr->first == "llsd-lsl-syntax-version") + { + // Skip over version key. + } + else + { + if (itr->second.isMap()) + { + processTokensGroup(itr->second, itr->first); + } + else + { + LL_WARNS("LSL-Tokens-Processing") << "Map for " + itr->first + " entries is missing! Ignoring." << LL_ENDL; + } + } + } + LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL; +} + +void LLKeywords::processTokensGroup(const LLSD& tokens, const std::string& group) +{ + LLColor4 color; + LLColor4 color_group; + LLColor4 color_deprecated = getColorGroup("deprecated"); + LLColor4 color_god_mode = getColorGroup("god-mode"); + + LLKeywordToken::ETokenType token_type = LLKeywordToken::TT_UNKNOWN; + // If a new token type is added here, it must also be added to the 'addToken' method + if (group == "constants") + { + token_type = LLKeywordToken::TT_CONSTANT; + } + else if (group == "controls") + { + token_type = LLKeywordToken::TT_CONTROL; + } + else if (group == "events") + { + token_type = LLKeywordToken::TT_EVENT; + } + else if (group == "functions") + { + token_type = LLKeywordToken::TT_FUNCTION; + } + else if (group == "label") + { + token_type = LLKeywordToken::TT_LABEL; + } + else if (group == "types") + { + token_type = LLKeywordToken::TT_TYPE; + } + + color_group = getColorGroup(group); + LL_DEBUGS("SyntaxLSL") << "Group: '" << group << "', using color: '" << color_group << "'" << LL_ENDL; + + if (tokens.isMap()) + { + LLSD::map_const_iterator outer_itr = tokens.beginMap(); + for ( ; outer_itr != tokens.endMap(); ++outer_itr ) + { + if (outer_itr->second.isMap()) + { + mAttributes.clear(); + LLSD arguments = LLSD(); + LLSD::map_const_iterator inner_itr = outer_itr->second.beginMap(); + for ( ; inner_itr != outer_itr->second.endMap(); ++inner_itr ) + { + if (inner_itr->first == "arguments") + { + if (inner_itr->second.isArray()) + { + arguments = inner_itr->second; + } + } + else if (!inner_itr->second.isMap() && !inner_itr->second.isArray()) + { + mAttributes[inner_itr->first] = inner_itr->second.asString(); + } + else + { + LL_WARNS("SyntaxLSL") << "Not a valid attribute: " << inner_itr->first << LL_ENDL; + } + } + + std::string tooltip = ""; + switch (token_type) + { + case LLKeywordToken::TT_CONSTANT: + if (getAttribute("type").length() > 0) + { + color_group = getColorGroup(group + "-" + getAttribute("type")); + } + else + { + color_group = getColorGroup(group); + } + tooltip = "Type: " + getAttribute("type") + ", Value: " + getAttribute("value"); + break; + case LLKeywordToken::TT_EVENT: + tooltip = outer_itr->first + "(" + getArguments(arguments) + ")"; + break; + case LLKeywordToken::TT_FUNCTION: + tooltip = getAttribute("return") + " " + outer_itr->first + "(" + getArguments(arguments) + ");"; + tooltip.append("\nEnergy: "); + tooltip.append(getAttribute("energy").empty() ? "0.0" : getAttribute("energy")); + if (!getAttribute("sleep").empty()) + { + tooltip += ", Sleep: " + getAttribute("sleep"); + } + default: + break; + } + + if (!getAttribute("tooltip").empty()) + { + if (!tooltip.empty()) + { + tooltip.append("\n"); + } + tooltip.append(getAttribute("tooltip")); + } + + color = getAttribute("deprecated") == "true" ? color_deprecated : color_group; + + if (getAttribute("god-mode") == "true") + { + color = color_god_mode; + } + + addToken(token_type, outer_itr->first, color, tooltip); + } + } + } + else if (tokens.isArray()) // Currently nothing should need this, but it's here for completeness + { + LL_INFOS("SyntaxLSL") << "Curious, shouldn't be an array here; adding all using color " << color << LL_ENDL; + for (S32 count = 0; count < tokens.size(); ++count) + { + addToken(token_type, tokens[count], color, ""); + } + } + else + { + LL_WARNS("SyntaxLSL") << "Invalid map/array passed: '" << tokens << "'" << LL_ENDL; + } +} + +LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other) +{ + if(other.mOwner) + { + copyData(other.mData, other.mLength); + } + else + { + mOwner = false; + mLength = other.mLength; + mData = other.mData; + } +} + +LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str) +{ + copyData(str.data(), str.size()); +} + +LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length) +: mData(start) +, mLength(length) +, mOwner(false) +{ +} + +LLKeywords::WStringMapIndex::~WStringMapIndex() +{ + if (mOwner) + { + delete[] mData; + } +} + +void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length) +{ + llwchar *data = new llwchar[length]; + memcpy((void*)data, (const void*)start, length * sizeof(llwchar)); + + mOwner = true; + mLength = length; + mData = data; +} + +bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const +{ + // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not. + // The comparison only needs to strictly order all possible strings, and be stable. + + bool result = false; + const llwchar* self_iter = mData; + const llwchar* self_end = mData + mLength; + const llwchar* other_iter = other.mData; + const llwchar* other_end = other.mData + other.mLength; + + while(true) + { + if(other_iter >= other_end) + { + // We've hit the end of other. + // This covers two cases: other being shorter than self, or the strings being equal. + // In either case, we want to return false. + result = false; + break; + } + else if(self_iter >= self_end) + { + // self is shorter than other. + result = true; + break; + } + else if(*self_iter != *other_iter) + { + // The current character differs. The strings are not equal. + result = *self_iter < *other_iter; + break; + } + + self_iter++; + other_iter++; + } + + return result; +} + +LLTrace::BlockTimerStatHandle FTM_SYNTAX_COLORING("Syntax Coloring"); + +// Walk through a string, applying the rules specified by the keyword token list and +// create a list of color segments. +void LLKeywords::findSegments(std::vector* seg_list, const LLWString& wtext, LLTextEditor& editor, LLStyleConstSP style) +{ + LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING); + seg_list->clear(); + + if( wtext.empty() ) + { + return; + } + + S32 text_len = wtext.size() + 1; + + seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); + + const llwchar* base = wtext.c_str(); + const llwchar* cur = base; + while( *cur ) + { + if( *cur == '\n' || cur == base ) + { + if( *cur == '\n' ) + { + LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, cur-base); + text_segment->setToken( 0 ); + insertSegment( *seg_list, text_segment, text_len, style, editor); + cur++; + if( !*cur || *cur == '\n' ) + { + continue; + } + } + + // Skip white space + while( *cur && iswspace(*cur) && (*cur != '\n') ) + { + cur++; + } + if( !*cur || *cur == '\n' ) + { + continue; + } + + // cur is now at the first non-whitespace character of a new line + + // Line start tokens + { + bool line_done = false; + for (token_list_t::iterator iter = mLineTokenList.begin(); + iter != mLineTokenList.end(); ++iter) + { + LLKeywordToken* cur_token = *iter; + if( cur_token->isHead( cur ) ) + { + S32 seg_start = cur - base; + while( *cur && *cur != '\n' ) + { + // skip the rest of the line + cur++; + } + S32 seg_end = cur - base; + + //create segments from seg_start to seg_end + insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor); + line_done = true; // to break out of second loop. + break; + } + } + + if( line_done ) + { + continue; + } + } + } + + // Skip white space + while( *cur && iswspace(*cur) && (*cur != '\n') ) + { + cur++; + } + + while( *cur && *cur != '\n' ) + { + // Check against delimiters + { + S32 seg_start = 0; + LLKeywordToken* cur_delimiter = NULL; + for (token_list_t::iterator iter = mDelimiterTokenList.begin(); + iter != mDelimiterTokenList.end(); ++iter) + { + LLKeywordToken* delimiter = *iter; + if( delimiter->isHead( cur ) ) + { + cur_delimiter = delimiter; + break; + } + } + + if( cur_delimiter ) + { + S32 between_delimiters = 0; + S32 seg_end = 0; + + seg_start = cur - base; + cur += cur_delimiter->getLengthHead(); + + LLKeywordToken::ETokenType type = cur_delimiter->getType(); + if( type == LLKeywordToken::TT_TWO_SIDED_DELIMITER || type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS ) + { + while( *cur && !cur_delimiter->isTail(cur)) + { + // Check for an escape sequence. + if (type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS && *cur == '\\') + { + // Count the number of backslashes. + S32 num_backslashes = 0; + while (*cur == '\\') + { + num_backslashes++; + between_delimiters++; + cur++; + } + // If the next character is the end delimiter? + if (cur_delimiter->isTail(cur)) + { + // If there was an odd number of backslashes, then this delimiter + // does not end the sequence. + if (num_backslashes % 2 == 1) + { + between_delimiters++; + cur++; + } + else + { + // This is an end delimiter. + break; + } + } + } + else + { + between_delimiters++; + cur++; + } + } + + if( *cur ) + { + cur += cur_delimiter->getLengthHead(); + seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead() + cur_delimiter->getLengthTail(); + } + else + { + // eof + seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead(); + } + } + else + { + llassert( cur_delimiter->getType() == LLKeywordToken::TT_ONE_SIDED_DELIMITER ); + // Left side is the delimiter. Right side is eol or eof. + while( *cur && ('\n' != *cur) ) + { + between_delimiters++; + cur++; + } + seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead(); + } + + insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, style, editor); + /* + LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor ); + text_segment->setToken( cur_delimiter ); + insertSegment( seg_list, text_segment, text_len, defaultColor, editor); + */ + // Note: we don't increment cur, since the end of one delimited seg may be immediately + // followed by the start of another one. + continue; + } + } + + // check against words + llwchar prev = cur > base ? *(cur-1) : 0; + if( !iswalnum( prev ) && (prev != '_') ) + { + const llwchar* p = cur; + while( iswalnum( *p ) || (*p == '_') ) + { + p++; + } + S32 seg_len = p - cur; + if( seg_len > 0 ) + { + WStringMapIndex word( cur, seg_len ); + word_token_map_t::iterator map_iter = mWordTokenMap.find(word); + if( map_iter != mWordTokenMap.end() ) + { + LLKeywordToken* cur_token = map_iter->second; + S32 seg_start = cur - base; + S32 seg_end = seg_start + seg_len; + + // LL_INFOS("SyntaxLSL") << "Seg: [" << word.c_str() << "]" << LL_ENDL; + + insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor); + } + cur += seg_len; + continue; + } + } + + if( *cur && *cur != '\n' ) + { + cur++; + } + } + } +} + +void LLKeywords::insertSegments(const LLWString& wtext, std::vector& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, LLStyleConstSP style, LLTextEditor& editor ) +{ + std::string::size_type pos = wtext.find('\n',seg_start); + + LLStyleConstSP cur_token_style = new LLStyle(LLStyle::Params().font(style->getFont()).color(cur_token->getColor())); + + while (pos!=-1 && pos < (std::string::size_type)seg_end) + { + if (pos!=seg_start) + { + LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, pos, editor); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len, style, editor); + } + + LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, pos); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len, style, editor); + + seg_start = pos+1; + pos = wtext.find('\n',seg_start); + } + + LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, seg_end, editor); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len, style, editor); +} + +void LLKeywords::insertSegment(std::vector& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor ) +{ + LLTextSegmentPtr last = seg_list.back(); + S32 new_seg_end = new_segment->getEnd(); + + if( new_segment->getStart() == last->getStart() ) + { + seg_list.pop_back(); + } + else + { + last->setEnd( new_segment->getStart() ); + } + seg_list.push_back( new_segment ); + + if( new_seg_end < text_len ) + { + seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) ); + } +} + +void LLKeywords::insertSegment(std::vector& seg_list, LLTextSegmentPtr new_segment, S32 text_len, LLStyleConstSP style, LLTextEditor& editor ) +{ + LLTextSegmentPtr last = seg_list.back(); + S32 new_seg_end = new_segment->getEnd(); + + if( new_segment->getStart() == last->getStart() ) + { + seg_list.pop_back(); + } + else + { + last->setEnd( new_segment->getStart() ); + } + seg_list.push_back( new_segment ); + + if( new_seg_end < text_len ) + { + seg_list.push_back( new LLNormalTextSegment( style, new_seg_end, text_len, editor ) ); + } +} + +#ifdef _DEBUG +void LLKeywords::dump() +{ + LL_INFOS() << "LLKeywords" << LL_ENDL; + + + LL_INFOS() << "LLKeywords::sWordTokenMap" << LL_ENDL; + word_token_map_t::iterator word_token_iter = mWordTokenMap.begin(); + while( word_token_iter != mWordTokenMap.end() ) + { + LLKeywordToken* word_token = word_token_iter->second; + word_token->dump(); + ++word_token_iter; + } + + LL_INFOS() << "LLKeywords::sLineTokenList" << LL_ENDL; + for (token_list_t::iterator iter = mLineTokenList.begin(); + iter != mLineTokenList.end(); ++iter) + { + LLKeywordToken* line_token = *iter; + line_token->dump(); + } + + + LL_INFOS() << "LLKeywords::sDelimiterTokenList" << LL_ENDL; + for (token_list_t::iterator iter = mDelimiterTokenList.begin(); + iter != mDelimiterTokenList.end(); ++iter) + { + LLKeywordToken* delimiter_token = *iter; + delimiter_token->dump(); + } +} + +void LLKeywordToken::dump() +{ + LL_INFOS() << "[" << + mColor.mV[VX] << ", " << + mColor.mV[VY] << ", " << + mColor.mV[VZ] << "] [" << + wstring_to_utf8str(mToken) << "]" << + LL_ENDL; +} + +#endif // DEBUG diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 1b6a785f27..6db9f64a97 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -1,1022 +1,1022 @@ -/** - * @file lllayoutstack.cpp - * @brief LLLayout class - dynamic stacking of UI elements - * - * $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$ - */ - -// Opaque view with a background and a border. Can contain LLUICtrls. - -#include "linden_common.h" - -#include "lllayoutstack.h" - -#include "lllocalcliprect.h" -#include "llpanel.h" -#include "llcriticaldamp.h" -#include "lliconctrl.h" -#include "boost/foreach.hpp" - -static const F32 MIN_FRACTIONAL_SIZE = 0.00001f; -static const F32 MAX_FRACTIONAL_SIZE = 1.f; - -static LLDefaultChildRegistry::Register register_layout_stack("layout_stack"); -static LLLayoutStack::LayoutStackRegistry::Register register_layout_panel("layout_panel"); - -// -// LLLayoutPanel -// -LLLayoutPanel::Params::Params() -: expanded_min_dim("expanded_min_dim", 0), - min_dim("min_dim", -1), - user_resize("user_resize", false), - auto_resize("auto_resize", true) -{ - addSynonym(min_dim, "min_width"); - addSynonym(min_dim, "min_height"); -} - -LLLayoutPanel::LLLayoutPanel(const Params& p) -: LLPanel(p), - mExpandedMinDim(p.expanded_min_dim.isProvided() ? p.expanded_min_dim : p.min_dim), - mMinDim(p.min_dim), - mAutoResize(p.auto_resize), - mUserResize(p.user_resize), - mCollapsed(false), - mCollapseAmt(0.f), - mVisibleAmt(1.f), // default to fully visible - mResizeBar(NULL), - mFractionalSize(0.f), - mTargetDim(0), - mIgnoreReshape(false), - mOrientation(LLLayoutStack::HORIZONTAL) -{ - // panels initialized as hidden should not start out partially visible - if (!getVisible()) - { - mVisibleAmt = 0.f; - } -} - -void LLLayoutPanel::initFromParams(const Params& p) -{ - LLPanel::initFromParams(p); - setFollowsNone(); -} - - -LLLayoutPanel::~LLLayoutPanel() -{ - // probably not necessary, but... - delete mResizeBar; - mResizeBar = NULL; - - gFocusMgr.removeKeyboardFocusWithoutCallback(this); -} - -F32 LLLayoutPanel::getAutoResizeFactor() const -{ - return mVisibleAmt * (1.f - mCollapseAmt); -} - -F32 LLLayoutPanel::getVisibleAmount() const -{ - return mVisibleAmt; -} - -S32 LLLayoutPanel::getLayoutDim() const -{ - return ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL) - ? getRect().getWidth() - : getRect().getHeight())); -} - -S32 LLLayoutPanel::getTargetDim() const -{ - return mTargetDim; -} - -void LLLayoutPanel::setTargetDim(S32 value) -{ - LLRect new_rect(getRect()); - if (mOrientation == LLLayoutStack::HORIZONTAL) - { - new_rect.mRight = new_rect.mLeft + value; - } - else - { - new_rect.mTop = new_rect.mBottom + value; - } - setShape(new_rect, true); -} - -S32 LLLayoutPanel::getVisibleDim() const -{ - F32 min_dim = getRelevantMinDim(); - return ll_round(mVisibleAmt - * (min_dim - + (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt)))); -} - -void LLLayoutPanel::setOrientation( LLView::EOrientation orientation ) -{ - mOrientation = orientation; - S32 layout_dim = ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL) - ? getRect().getWidth() - : getRect().getHeight())); - - if (!mAutoResize && mUserResize && mMinDim == -1) - { - setMinDim(layout_dim); - } - mTargetDim = llmax(layout_dim, getMinDim()); -} - -void LLLayoutPanel::setVisible( bool visible ) -{ - if (visible != getVisible()) - { - LLLayoutStack* stackp = dynamic_cast(getParent()); - if (stackp) - { - stackp->mNeedsLayout = true; - } - } - LLPanel::setVisible(visible); -} - -void LLLayoutPanel::reshape( S32 width, S32 height, bool called_from_parent /*= true*/ ) -{ - if (width == getRect().getWidth() && height == getRect().getHeight() && !LLView::sForceReshape) return; - - if (!mIgnoreReshape && !mAutoResize) - { - mTargetDim = (mOrientation == LLLayoutStack::HORIZONTAL) ? width : height; - LLLayoutStack* stackp = dynamic_cast(getParent()); - if (stackp) - { - stackp->mNeedsLayout = true; - } - } - LLPanel::reshape(width, height, called_from_parent); -} - -void LLLayoutPanel::handleReshape(const LLRect& new_rect, bool by_user) -{ - LLLayoutStack* stackp = dynamic_cast(getParent()); - if (stackp) - { - if (by_user) - { // tell layout stack to account for new shape - - // make sure that panels have already been auto resized - stackp->updateLayout(); - // now apply requested size to panel - stackp->updatePanelRect(this, new_rect); - } - stackp->mNeedsLayout = true; - } - LLPanel::handleReshape(new_rect, by_user); -} - -// -// LLLayoutStack -// - -LLLayoutStack::Params::Params() -: orientation("orientation"), - animate("animate", true), - clip("clip", true), - open_time_constant("open_time_constant", 0.02f), - close_time_constant("close_time_constant", 0.03f), - resize_bar_overlap("resize_bar_overlap", 1), - border_size("border_size", LLCachedControl(*LLUI::getInstance()->mSettingGroups["config"], "UIResizeBarHeight", 0)), - show_drag_handle("show_drag_handle", false), - drag_handle_first_indent("drag_handle_first_indent", 0), - drag_handle_second_indent("drag_handle_second_indent", 0), - drag_handle_thickness("drag_handle_thickness", 5), - drag_handle_shift("drag_handle_shift", 2), - drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody")) -{ - addSynonym(border_size, "drag_handle_gap"); -} - -LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) -: LLView(p), - mPanelSpacing(p.border_size), - mOrientation(p.orientation), - mAnimate(p.animate), - mAnimatedThisFrame(false), - mNeedsLayout(true), - mClip(p.clip), - mOpenTimeConstant(p.open_time_constant), - mCloseTimeConstant(p.close_time_constant), - mResizeBarOverlap(p.resize_bar_overlap), - mShowDragHandle(p.show_drag_handle), - mDragHandleFirstIndent(p.drag_handle_first_indent), - mDragHandleSecondIndent(p.drag_handle_second_indent), - mDragHandleThickness(p.drag_handle_thickness), - mDragHandleShift(p.drag_handle_shift), - mDragHandleColor(p.drag_handle_color()) -{ -} - -LLLayoutStack::~LLLayoutStack() -{ - e_panel_list_t panels = mPanels; // copy list of panel pointers - mPanels.clear(); // clear so that removeChild() calls don't cause trouble - std::for_each(panels.begin(), panels.end(), DeletePointer()); -} - -void LLLayoutStack::draw() -{ - updateLayout(); - - // always clip to stack itself - LLLocalClipRect clip(getLocalRect()); - for (LLLayoutPanel* panelp : mPanels) - { - if ((!panelp->getVisible() || panelp->mCollapsed) - && (panelp->mVisibleAmt < 0.001f || !mAnimate)) - { - // essentially invisible - continue; - } - // clip to layout rectangle, not bounding rectangle - LLRect clip_rect = panelp->getRect(); - // scale clipping rectangle by visible amount - if (mOrientation == HORIZONTAL) - { - clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim(); - } - else - { - clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim(); - } - - {LLLocalClipRect clip(clip_rect, mClip); - // only force drawing invisible children if visible amount is non-zero - drawChild(panelp, 0, 0, !clip_rect.isEmpty()); - } - if (panelp->getResizeBar()->getVisible()) - { - drawChild(panelp->getResizeBar()); - } - } -} - -void LLLayoutStack::deleteAllChildren() -{ - mPanels.clear(); - LLView::deleteAllChildren(); - - // Not really needed since nothing is left to - // display, but for the sake of consistency - updateFractionalSizes(); - mNeedsLayout = true; -} - -void LLLayoutStack::removeChild(LLView* view) -{ - LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast(view)); - - if (embedded_panelp) - { - mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); - LLView::removeChild(view); - updateFractionalSizes(); - mNeedsLayout = true; - } - else - { - LLView::removeChild(view); - } -} - -bool LLLayoutStack::postBuild() -{ - updateLayout(); - return true; -} - -bool LLLayoutStack::addChild(LLView* child, S32 tab_group) -{ - LLLayoutPanel* panelp = dynamic_cast(child); - if (panelp) - { - panelp->setOrientation(mOrientation); - mPanels.push_back(panelp); - createResizeBar(panelp); - mNeedsLayout = true; - } - bool result = LLView::addChild(child, tab_group); - - updateFractionalSizes(); - return result; -} - -void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate) -{ - addChild(panel); - - // panel starts off invisible (collapsed) - if (animate == ANIMATE) - { - panel->mVisibleAmt = 0.f; - panel->setVisible(true); - } -} - -void LLLayoutStack::collapsePanel(LLPanel* panel, bool collapsed) -{ - LLLayoutPanel* panel_container = findEmbeddedPanel(panel); - if (!panel_container) return; - - panel_container->mCollapsed = collapsed; - mNeedsLayout = true; -} - -class LLImagePanel : public LLPanel -{ -public: - struct Params : public LLInitParam::Block - { - Optional horizontal; - Params() : horizontal("horizontal", false) {} - }; - LLImagePanel(const Params& p) : LLPanel(p), mHorizontal(p.horizontal) {} - virtual ~LLImagePanel() {} - - void draw() - { - const LLRect& parent_rect = getParent()->getRect(); - const LLRect& rect = getRect(); - LLRect clip_rect( -rect.mLeft, parent_rect.getHeight() - rect.mBottom - 2 - , parent_rect.getWidth() - rect.mLeft - (mHorizontal ? 2 : 0), -rect.mBottom); - LLLocalClipRect clip(clip_rect); - LLPanel::draw(); - } - -private: - bool mHorizontal; -}; - -void LLLayoutStack::updateLayout() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - - if (!mNeedsLayout) return; - - bool continue_animating = animatePanels(); - F32 total_visible_fraction = 0.f; - S32 space_to_distribute = (mOrientation == HORIZONTAL) - ? getRect().getWidth() - : getRect().getHeight(); - - // first, assign minimum dimensions - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - panelp->mTargetDim = panelp->getRelevantMinDim(); - } - space_to_distribute -= panelp->getVisibleDim() + ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()); - total_visible_fraction += panelp->mFractionalSize * panelp->getAutoResizeFactor(); - } - - llassert(total_visible_fraction < 1.05f); - - // don't need spacing after last panel - if (!mPanels.empty()) - { - space_to_distribute += ll_round(F32(mPanelSpacing) * mPanels.back()->getVisibleAmount()); - } - - S32 remaining_space = space_to_distribute; - if (space_to_distribute > 0 && total_visible_fraction > 0.f) - { // give space proportionally to visible auto resize panels - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction); - S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute); - panelp->mTargetDim += delta; - remaining_space -= delta; - } - } - } - - // distribute any left over pixels to non-collapsed, visible panels - for (LLLayoutPanel* panelp : mPanels) - { - if (remaining_space == 0) break; - - if (panelp->mAutoResize - && !panelp->mCollapsed - && panelp->getVisible()) - { - S32 space_for_panel = remaining_space > 0 ? 1 : -1; - panelp->mTargetDim += space_for_panel; - remaining_space -= space_for_panel; - } - } - - F32 cur_pos = (mOrientation == HORIZONTAL) ? 0.f : (F32)getRect().getHeight(); - - for (LLLayoutPanel* panelp : mPanels) - { - F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim); - - LLRect panel_rect; - if (mOrientation == HORIZONTAL) - { - panel_rect.setLeftTopAndSize(ll_round(cur_pos), - getRect().getHeight(), - ll_round(panel_dim), - getRect().getHeight()); - } - else - { - panel_rect.setLeftTopAndSize(0, - ll_round(cur_pos), - getRect().getWidth(), - ll_round(panel_dim)); - } - - LLRect resize_bar_rect(panel_rect); - F32 panel_spacing = (F32)mPanelSpacing * panelp->getVisibleAmount(); - F32 panel_visible_dim = panelp->getVisibleDim(); - S32 panel_spacing_round = (S32)(ll_round(panel_spacing)); - - if (mOrientation == HORIZONTAL) - { - cur_pos += panel_visible_dim + panel_spacing; - - if (mShowDragHandle && panel_spacing_round > mDragHandleThickness) - { - resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift; - resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness; - } - else - { - resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap; - resize_bar_rect.mRight = panel_rect.mRight + panel_spacing_round + mResizeBarOverlap; - } - - if (mShowDragHandle) - { - resize_bar_rect.mBottom += mDragHandleSecondIndent; - resize_bar_rect.mTop -= mDragHandleFirstIndent; - } - - } - else //VERTICAL - { - cur_pos -= panel_visible_dim + panel_spacing; - - if (mShowDragHandle && panel_spacing_round > mDragHandleThickness) - { - resize_bar_rect.mTop = panel_rect.mBottom - mDragHandleShift; - resize_bar_rect.mBottom = resize_bar_rect.mTop - mDragHandleThickness; - } - else - { - resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap; - resize_bar_rect.mBottom = panel_rect.mBottom - panel_spacing_round - mResizeBarOverlap; - } - - if (mShowDragHandle) - { - resize_bar_rect.mLeft += mDragHandleFirstIndent; - resize_bar_rect.mRight -= mDragHandleSecondIndent; - } - } - - panelp->setIgnoreReshape(true); - panelp->setShape(panel_rect); - panelp->setIgnoreReshape(false); - panelp->mResizeBar->setShape(resize_bar_rect); - } - - updateResizeBarLimits(); - - // clear animation flag at end, since panel resizes will set it - // and leave it set if there is any animation in progress - mNeedsLayout = continue_animating; -} // end LLLayoutStack::updateLayout - -void LLLayoutStack::setPanelSpacing(S32 val) -{ - if (mPanelSpacing != val) - { - mPanelSpacing = val; - mNeedsLayout = true; - } -} - -LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const -{ - if (!panelp) return NULL; - - for (LLLayoutPanel* p : mPanels) - { - if (p == panelp) - { - return p; - } - } - return NULL; -} - -LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const -{ - LLLayoutPanel* result = NULL; - - for (LLLayoutPanel* p : mPanels) - { - if (p->getName() == name) - { - result = p; - break; - } - } - - return result; -} - -void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) -{ - for (LLLayoutPanel* lp : mPanels) - { - if (lp->mResizeBar == NULL) - { - LLResizeBar::Params resize_params; - resize_params.name("resize"); - resize_params.resizing_view(lp); - resize_params.min_size(lp->getRelevantMinDim()); - resize_params.side((mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM); - resize_params.snapping_enabled(false); - LLResizeBar* resize_bar = LLUICtrlFactory::create(resize_params); - lp->mResizeBar = resize_bar; - - if (mShowDragHandle) - { - LLPanel::Params resize_bar_bg_panel_p; - resize_bar_bg_panel_p.name = "resize_handle_bg_panel"; - resize_bar_bg_panel_p.rect = lp->mResizeBar->getLocalRect(); - resize_bar_bg_panel_p.follows.flags = FOLLOWS_ALL; - resize_bar_bg_panel_p.tab_stop = false; - resize_bar_bg_panel_p.background_visible = true; - resize_bar_bg_panel_p.bg_alpha_color = mDragHandleColor; - resize_bar_bg_panel_p.has_border = true; - resize_bar_bg_panel_p.border.border_thickness = 1; - resize_bar_bg_panel_p.border.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); - resize_bar_bg_panel_p.border.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark"); - - LLPanel* resize_bar_bg_panel = LLUICtrlFactory::create(resize_bar_bg_panel_p); - - LLIconCtrl::Params icon_p; - icon_p.name = "resize_handle_image"; - icon_p.rect = lp->mResizeBar->getLocalRect(); - icon_p.follows.flags = FOLLOWS_ALL; - icon_p.image = LLUI::getUIImage(mOrientation == HORIZONTAL ? "Vertical Drag Handle" : "Horizontal Drag Handle"); - resize_bar_bg_panel->addChild(LLUICtrlFactory::create(icon_p)); - - lp->mResizeBar->addChild(resize_bar_bg_panel); - } - - /*if (mShowDragHandle) - { - LLViewBorder::Params border_params; - border_params.border_thickness = 1; - border_params.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); - border_params.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark"); - - addBorder(border_params); - setBorderVisible(true); - - LLImagePanel::Params image_panel; - mDragHandleImage = LLUI::getUIImage(LLResizeBar::RIGHT == mSide ? "Vertical Drag Handle" : "Horizontal Drag Handle"); - image_panel.bg_alpha_image = mDragHandleImage; - image_panel.background_visible = true; - image_panel.horizontal = (LLResizeBar::BOTTOM == mSide); - mImagePanel = LLUICtrlFactory::create(image_panel); - setImagePanel(mImagePanel); - }*/ - - //if (mShowDragHandle) - //{ - // setBackgroundVisible(true); - // setTransparentColor(LLUIColorTable::instance().getColor("ResizebarBody")); - //} - - /*if (mShowDragHandle) - { - S32 image_width = mDragHandleImage->getTextureWidth(); - S32 image_height = mDragHandleImage->getTextureHeight(); - const LLRect& panel_rect = getRect(); - S32 image_left = (panel_rect.getWidth() - image_width) / 2 - 1; - S32 image_bottom = (panel_rect.getHeight() - image_height) / 2; - mImagePanel->setRect(LLRect(image_left, image_bottom + image_height, image_left + image_width, image_bottom)); - }*/ - LLView::addChild(resize_bar, 0); - } - } - // bring all resize bars to the front so that they are clickable even over the panels - // with a bit of overlap - for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLResizeBar* resize_barp = (*panel_it)->mResizeBar; - sendChildToFront(resize_barp); - } -} - -// update layout stack animations, etc. once per frame -// NOTE: we use this to size world view based on animating UI, *before* we draw the UI -// we might still need to call updateLayout during UI draw phase, in case UI elements -// are resizing themselves dynamically -//static -void LLLayoutStack::updateClass() -{ - for (auto& layout : instance_snapshot()) - { - layout.updateLayout(); - layout.mAnimatedThisFrame = false; - } -} - -void LLLayoutStack::updateFractionalSizes() -{ - F32 total_resizable_dim = 0.f; - - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); - } - } - - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - F32 panel_resizable_dim = llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); - panelp->mFractionalSize = panel_resizable_dim > 0.f - ? llclamp(panel_resizable_dim / total_resizable_dim, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE) - : MIN_FRACTIONAL_SIZE; - llassert(!llisnan(panelp->mFractionalSize)); - } - } - - normalizeFractionalSizes(); -} - - -void LLLayoutStack::normalizeFractionalSizes() -{ - S32 num_auto_resize_panels = 0; - F32 total_fractional_size = 0.f; - - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - total_fractional_size += panelp->mFractionalSize; - num_auto_resize_panels++; - } - } - - if (total_fractional_size == 0.f) - { // equal distribution - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels; - } - } - } - else - { // renormalize - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - panelp->mFractionalSize /= total_fractional_size; - } - } - } -} - -bool LLLayoutStack::animatePanels() -{ - bool continue_animating = false; - - // - // animate visibility - // - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->getVisible()) - { - if (mAnimate && panelp->mVisibleAmt < 1.f) - { - if (!mAnimatedThisFrame) - { - panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLSmoothInterpolation::getInterpolant(mOpenTimeConstant)); - if (panelp->mVisibleAmt > 0.99f) - { - panelp->mVisibleAmt = 1.f; - } - } - - mAnimatedThisFrame = true; - continue_animating = true; - } - else - { - if (panelp->mVisibleAmt != 1.f) - { - panelp->mVisibleAmt = 1.f; - mAnimatedThisFrame = true; - } - } - } - else // not visible - { - if (mAnimate && panelp->mVisibleAmt > 0.f) - { - if (!mAnimatedThisFrame) - { - panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant)); - if (panelp->mVisibleAmt < 0.001f) - { - panelp->mVisibleAmt = 0.f; - } - } - - continue_animating = true; - mAnimatedThisFrame = true; - } - else - { - if (panelp->mVisibleAmt != 0.f) - { - panelp->mVisibleAmt = 0.f; - mAnimatedThisFrame = true; - } - } - } - - F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f; - if (panelp->mCollapseAmt != collapse_state) - { - if (mAnimate) - { - if (!mAnimatedThisFrame) - { - panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant)); - } - - if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f) - { - panelp->mCollapseAmt = collapse_state; - } - - mAnimatedThisFrame = true; - continue_animating = true; - } - else - { - panelp->mCollapseAmt = collapse_state; - mAnimatedThisFrame = true; - } - } - } - - if (mAnimatedThisFrame) mNeedsLayout = true; - return continue_animating; -} - -void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& new_rect ) -{ - S32 new_dim = (mOrientation == HORIZONTAL) - ? new_rect.getWidth() - : new_rect.getHeight(); - S32 delta_panel_dim = new_dim - resized_panel->getVisibleDim(); - if (delta_panel_dim == 0) return; - - F32 total_visible_fraction = 0.f; - F32 delta_auto_resize_headroom = 0.f; - F32 old_auto_resize_headroom = 0.f; - - LLLayoutPanel* other_resize_panel = NULL; - LLLayoutPanel* following_panel = NULL; - - BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available... - { - if (panelp->mAutoResize) - { - old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim()); - if (panelp->getVisible() && !panelp->mCollapsed) - { - total_visible_fraction += panelp->mFractionalSize; - } - } - - if (panelp == resized_panel) - { - other_resize_panel = following_panel; - } - - if (panelp->getVisible() && !panelp->mCollapsed) - { - following_panel = panelp; - } - } - - if (resized_panel->mAutoResize) - { - if (!other_resize_panel || !other_resize_panel->mAutoResize) - { - delta_auto_resize_headroom += delta_panel_dim; - } - } - else - { - if (!other_resize_panel || other_resize_panel->mAutoResize) - { - delta_auto_resize_headroom -= delta_panel_dim; - } - } - - F32 fraction_given_up = 0.f; - F32 fraction_remaining = 1.f; - F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom; - - enum - { - BEFORE_RESIZED_PANEL, - RESIZED_PANEL, - NEXT_PANEL, - AFTER_RESIZED_PANEL - } which_panel = BEFORE_RESIZED_PANEL; - - for (LLLayoutPanel* panelp : mPanels) - { - if (!panelp->getVisible() || panelp->mCollapsed) - { - if (panelp->mAutoResize) - { - fraction_remaining -= panelp->mFractionalSize; - } - continue; - } - - if (panelp == resized_panel) - { - which_panel = RESIZED_PANEL; - } - - switch(which_panel) - { - case BEFORE_RESIZED_PANEL: - if (panelp->mAutoResize) - { // freeze current size as fraction of overall auto_resize space - F32 fractional_adjustment_factor = new_auto_resize_headroom == 0.f - ? 1.f - : old_auto_resize_headroom / new_auto_resize_headroom; - F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor, - MIN_FRACTIONAL_SIZE, - MAX_FRACTIONAL_SIZE); - fraction_given_up -= new_fractional_size - panelp->mFractionalSize; - fraction_remaining -= panelp->mFractionalSize; - panelp->mFractionalSize = new_fractional_size; - llassert(!llisnan(panelp->mFractionalSize)); - } - else - { - // leave non auto-resize panels alone - } - break; - case RESIZED_PANEL: - if (panelp->mAutoResize) - { // freeze new size as fraction - F32 new_fractional_size = (new_auto_resize_headroom == 0.f) - ? MAX_FRACTIONAL_SIZE - : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / new_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); - fraction_given_up -= new_fractional_size - panelp->mFractionalSize; - fraction_remaining -= panelp->mFractionalSize; - panelp->mFractionalSize = new_fractional_size; - llassert(!llisnan(panelp->mFractionalSize)); - } - else - { // freeze new size as original size - panelp->mTargetDim = new_dim; - } - which_panel = NEXT_PANEL; - break; - case NEXT_PANEL: - if (panelp->mAutoResize) - { - fraction_remaining -= panelp->mFractionalSize; - if (resized_panel->mAutoResize) - { - panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); - fraction_given_up = 0.f; - } - else - { - if (new_auto_resize_headroom < 1.f) - { - new_auto_resize_headroom = 1.f; - } - - F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) - / new_auto_resize_headroom, - MIN_FRACTIONAL_SIZE, - MAX_FRACTIONAL_SIZE); - fraction_given_up -= new_fractional_size - panelp->mFractionalSize; - panelp->mFractionalSize = new_fractional_size; - } - } - else - { - panelp->mTargetDim -= delta_panel_dim; - } - which_panel = AFTER_RESIZED_PANEL; - break; - case AFTER_RESIZED_PANEL: - if (panelp->mAutoResize && fraction_given_up != 0.f) - { - panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up, - MIN_FRACTIONAL_SIZE, - MAX_FRACTIONAL_SIZE); - } - break; - default: - break; - } - } - updateLayout(); - //normalizeFractionalSizes(); -} - -void LLLayoutStack::reshape(S32 width, S32 height, bool called_from_parent) -{ - mNeedsLayout = true; - LLView::reshape(width, height, called_from_parent); -} - -void LLLayoutStack::updateResizeBarLimits() -{ - LLLayoutPanel* previous_visible_panelp{ nullptr }; - BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available... - { - if (!visible_panelp->getVisible() || visible_panelp->mCollapsed) - { - visible_panelp->mResizeBar->setVisible(false); - continue; - } - - // toggle resize bars based on panel visibility, resizability, etc - if (previous_visible_panelp - && (visible_panelp->mUserResize || previous_visible_panelp->mUserResize) // one of the pair is user resizable - && (visible_panelp->mAutoResize || visible_panelp->mUserResize) // current panel is resizable - && (previous_visible_panelp->mAutoResize || previous_visible_panelp->mUserResize)) // previous panel is resizable - { - visible_panelp->mResizeBar->setVisible(true); - S32 previous_panel_headroom = previous_visible_panelp->getVisibleDim() - previous_visible_panelp->getRelevantMinDim(); - visible_panelp->mResizeBar->setResizeLimits(visible_panelp->getRelevantMinDim(), - visible_panelp->getVisibleDim() + previous_panel_headroom); - } - else - { - visible_panelp->mResizeBar->setVisible(false); - } - - previous_visible_panelp = visible_panelp; - } -} - +/** + * @file lllayoutstack.cpp + * @brief LLLayout class - dynamic stacking of UI elements + * + * $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$ + */ + +// Opaque view with a background and a border. Can contain LLUICtrls. + +#include "linden_common.h" + +#include "lllayoutstack.h" + +#include "lllocalcliprect.h" +#include "llpanel.h" +#include "llcriticaldamp.h" +#include "lliconctrl.h" +#include "boost/foreach.hpp" + +static const F32 MIN_FRACTIONAL_SIZE = 0.00001f; +static const F32 MAX_FRACTIONAL_SIZE = 1.f; + +static LLDefaultChildRegistry::Register register_layout_stack("layout_stack"); +static LLLayoutStack::LayoutStackRegistry::Register register_layout_panel("layout_panel"); + +// +// LLLayoutPanel +// +LLLayoutPanel::Params::Params() +: expanded_min_dim("expanded_min_dim", 0), + min_dim("min_dim", -1), + user_resize("user_resize", false), + auto_resize("auto_resize", true) +{ + addSynonym(min_dim, "min_width"); + addSynonym(min_dim, "min_height"); +} + +LLLayoutPanel::LLLayoutPanel(const Params& p) +: LLPanel(p), + mExpandedMinDim(p.expanded_min_dim.isProvided() ? p.expanded_min_dim : p.min_dim), + mMinDim(p.min_dim), + mAutoResize(p.auto_resize), + mUserResize(p.user_resize), + mCollapsed(false), + mCollapseAmt(0.f), + mVisibleAmt(1.f), // default to fully visible + mResizeBar(NULL), + mFractionalSize(0.f), + mTargetDim(0), + mIgnoreReshape(false), + mOrientation(LLLayoutStack::HORIZONTAL) +{ + // panels initialized as hidden should not start out partially visible + if (!getVisible()) + { + mVisibleAmt = 0.f; + } +} + +void LLLayoutPanel::initFromParams(const Params& p) +{ + LLPanel::initFromParams(p); + setFollowsNone(); +} + + +LLLayoutPanel::~LLLayoutPanel() +{ + // probably not necessary, but... + delete mResizeBar; + mResizeBar = NULL; + + gFocusMgr.removeKeyboardFocusWithoutCallback(this); +} + +F32 LLLayoutPanel::getAutoResizeFactor() const +{ + return mVisibleAmt * (1.f - mCollapseAmt); +} + +F32 LLLayoutPanel::getVisibleAmount() const +{ + return mVisibleAmt; +} + +S32 LLLayoutPanel::getLayoutDim() const +{ + return ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight())); +} + +S32 LLLayoutPanel::getTargetDim() const +{ + return mTargetDim; +} + +void LLLayoutPanel::setTargetDim(S32 value) +{ + LLRect new_rect(getRect()); + if (mOrientation == LLLayoutStack::HORIZONTAL) + { + new_rect.mRight = new_rect.mLeft + value; + } + else + { + new_rect.mTop = new_rect.mBottom + value; + } + setShape(new_rect, true); +} + +S32 LLLayoutPanel::getVisibleDim() const +{ + F32 min_dim = getRelevantMinDim(); + return ll_round(mVisibleAmt + * (min_dim + + (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt)))); +} + +void LLLayoutPanel::setOrientation( LLView::EOrientation orientation ) +{ + mOrientation = orientation; + S32 layout_dim = ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight())); + + if (!mAutoResize && mUserResize && mMinDim == -1) + { + setMinDim(layout_dim); + } + mTargetDim = llmax(layout_dim, getMinDim()); +} + +void LLLayoutPanel::setVisible( bool visible ) +{ + if (visible != getVisible()) + { + LLLayoutStack* stackp = dynamic_cast(getParent()); + if (stackp) + { + stackp->mNeedsLayout = true; + } + } + LLPanel::setVisible(visible); +} + +void LLLayoutPanel::reshape( S32 width, S32 height, bool called_from_parent /*= true*/ ) +{ + if (width == getRect().getWidth() && height == getRect().getHeight() && !LLView::sForceReshape) return; + + if (!mIgnoreReshape && !mAutoResize) + { + mTargetDim = (mOrientation == LLLayoutStack::HORIZONTAL) ? width : height; + LLLayoutStack* stackp = dynamic_cast(getParent()); + if (stackp) + { + stackp->mNeedsLayout = true; + } + } + LLPanel::reshape(width, height, called_from_parent); +} + +void LLLayoutPanel::handleReshape(const LLRect& new_rect, bool by_user) +{ + LLLayoutStack* stackp = dynamic_cast(getParent()); + if (stackp) + { + if (by_user) + { // tell layout stack to account for new shape + + // make sure that panels have already been auto resized + stackp->updateLayout(); + // now apply requested size to panel + stackp->updatePanelRect(this, new_rect); + } + stackp->mNeedsLayout = true; + } + LLPanel::handleReshape(new_rect, by_user); +} + +// +// LLLayoutStack +// + +LLLayoutStack::Params::Params() +: orientation("orientation"), + animate("animate", true), + clip("clip", true), + open_time_constant("open_time_constant", 0.02f), + close_time_constant("close_time_constant", 0.03f), + resize_bar_overlap("resize_bar_overlap", 1), + border_size("border_size", LLCachedControl(*LLUI::getInstance()->mSettingGroups["config"], "UIResizeBarHeight", 0)), + show_drag_handle("show_drag_handle", false), + drag_handle_first_indent("drag_handle_first_indent", 0), + drag_handle_second_indent("drag_handle_second_indent", 0), + drag_handle_thickness("drag_handle_thickness", 5), + drag_handle_shift("drag_handle_shift", 2), + drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody")) +{ + addSynonym(border_size, "drag_handle_gap"); +} + +LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) +: LLView(p), + mPanelSpacing(p.border_size), + mOrientation(p.orientation), + mAnimate(p.animate), + mAnimatedThisFrame(false), + mNeedsLayout(true), + mClip(p.clip), + mOpenTimeConstant(p.open_time_constant), + mCloseTimeConstant(p.close_time_constant), + mResizeBarOverlap(p.resize_bar_overlap), + mShowDragHandle(p.show_drag_handle), + mDragHandleFirstIndent(p.drag_handle_first_indent), + mDragHandleSecondIndent(p.drag_handle_second_indent), + mDragHandleThickness(p.drag_handle_thickness), + mDragHandleShift(p.drag_handle_shift), + mDragHandleColor(p.drag_handle_color()) +{ +} + +LLLayoutStack::~LLLayoutStack() +{ + e_panel_list_t panels = mPanels; // copy list of panel pointers + mPanels.clear(); // clear so that removeChild() calls don't cause trouble + std::for_each(panels.begin(), panels.end(), DeletePointer()); +} + +void LLLayoutStack::draw() +{ + updateLayout(); + + // always clip to stack itself + LLLocalClipRect clip(getLocalRect()); + for (LLLayoutPanel* panelp : mPanels) + { + if ((!panelp->getVisible() || panelp->mCollapsed) + && (panelp->mVisibleAmt < 0.001f || !mAnimate)) + { + // essentially invisible + continue; + } + // clip to layout rectangle, not bounding rectangle + LLRect clip_rect = panelp->getRect(); + // scale clipping rectangle by visible amount + if (mOrientation == HORIZONTAL) + { + clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim(); + } + else + { + clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim(); + } + + {LLLocalClipRect clip(clip_rect, mClip); + // only force drawing invisible children if visible amount is non-zero + drawChild(panelp, 0, 0, !clip_rect.isEmpty()); + } + if (panelp->getResizeBar()->getVisible()) + { + drawChild(panelp->getResizeBar()); + } + } +} + +void LLLayoutStack::deleteAllChildren() +{ + mPanels.clear(); + LLView::deleteAllChildren(); + + // Not really needed since nothing is left to + // display, but for the sake of consistency + updateFractionalSizes(); + mNeedsLayout = true; +} + +void LLLayoutStack::removeChild(LLView* view) +{ + LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast(view)); + + if (embedded_panelp) + { + mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); + LLView::removeChild(view); + updateFractionalSizes(); + mNeedsLayout = true; + } + else + { + LLView::removeChild(view); + } +} + +bool LLLayoutStack::postBuild() +{ + updateLayout(); + return true; +} + +bool LLLayoutStack::addChild(LLView* child, S32 tab_group) +{ + LLLayoutPanel* panelp = dynamic_cast(child); + if (panelp) + { + panelp->setOrientation(mOrientation); + mPanels.push_back(panelp); + createResizeBar(panelp); + mNeedsLayout = true; + } + bool result = LLView::addChild(child, tab_group); + + updateFractionalSizes(); + return result; +} + +void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate) +{ + addChild(panel); + + // panel starts off invisible (collapsed) + if (animate == ANIMATE) + { + panel->mVisibleAmt = 0.f; + panel->setVisible(true); + } +} + +void LLLayoutStack::collapsePanel(LLPanel* panel, bool collapsed) +{ + LLLayoutPanel* panel_container = findEmbeddedPanel(panel); + if (!panel_container) return; + + panel_container->mCollapsed = collapsed; + mNeedsLayout = true; +} + +class LLImagePanel : public LLPanel +{ +public: + struct Params : public LLInitParam::Block + { + Optional horizontal; + Params() : horizontal("horizontal", false) {} + }; + LLImagePanel(const Params& p) : LLPanel(p), mHorizontal(p.horizontal) {} + virtual ~LLImagePanel() {} + + void draw() + { + const LLRect& parent_rect = getParent()->getRect(); + const LLRect& rect = getRect(); + LLRect clip_rect( -rect.mLeft, parent_rect.getHeight() - rect.mBottom - 2 + , parent_rect.getWidth() - rect.mLeft - (mHorizontal ? 2 : 0), -rect.mBottom); + LLLocalClipRect clip(clip_rect); + LLPanel::draw(); + } + +private: + bool mHorizontal; +}; + +void LLLayoutStack::updateLayout() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + if (!mNeedsLayout) return; + + bool continue_animating = animatePanels(); + F32 total_visible_fraction = 0.f; + S32 space_to_distribute = (mOrientation == HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight(); + + // first, assign minimum dimensions + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->mAutoResize) + { + panelp->mTargetDim = panelp->getRelevantMinDim(); + } + space_to_distribute -= panelp->getVisibleDim() + ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()); + total_visible_fraction += panelp->mFractionalSize * panelp->getAutoResizeFactor(); + } + + llassert(total_visible_fraction < 1.05f); + + // don't need spacing after last panel + if (!mPanels.empty()) + { + space_to_distribute += ll_round(F32(mPanelSpacing) * mPanels.back()->getVisibleAmount()); + } + + S32 remaining_space = space_to_distribute; + if (space_to_distribute > 0 && total_visible_fraction > 0.f) + { // give space proportionally to visible auto resize panels + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->mAutoResize) + { + F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction); + S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute); + panelp->mTargetDim += delta; + remaining_space -= delta; + } + } + } + + // distribute any left over pixels to non-collapsed, visible panels + for (LLLayoutPanel* panelp : mPanels) + { + if (remaining_space == 0) break; + + if (panelp->mAutoResize + && !panelp->mCollapsed + && panelp->getVisible()) + { + S32 space_for_panel = remaining_space > 0 ? 1 : -1; + panelp->mTargetDim += space_for_panel; + remaining_space -= space_for_panel; + } + } + + F32 cur_pos = (mOrientation == HORIZONTAL) ? 0.f : (F32)getRect().getHeight(); + + for (LLLayoutPanel* panelp : mPanels) + { + F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim); + + LLRect panel_rect; + if (mOrientation == HORIZONTAL) + { + panel_rect.setLeftTopAndSize(ll_round(cur_pos), + getRect().getHeight(), + ll_round(panel_dim), + getRect().getHeight()); + } + else + { + panel_rect.setLeftTopAndSize(0, + ll_round(cur_pos), + getRect().getWidth(), + ll_round(panel_dim)); + } + + LLRect resize_bar_rect(panel_rect); + F32 panel_spacing = (F32)mPanelSpacing * panelp->getVisibleAmount(); + F32 panel_visible_dim = panelp->getVisibleDim(); + S32 panel_spacing_round = (S32)(ll_round(panel_spacing)); + + if (mOrientation == HORIZONTAL) + { + cur_pos += panel_visible_dim + panel_spacing; + + if (mShowDragHandle && panel_spacing_round > mDragHandleThickness) + { + resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift; + resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness; + } + else + { + resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap; + resize_bar_rect.mRight = panel_rect.mRight + panel_spacing_round + mResizeBarOverlap; + } + + if (mShowDragHandle) + { + resize_bar_rect.mBottom += mDragHandleSecondIndent; + resize_bar_rect.mTop -= mDragHandleFirstIndent; + } + + } + else //VERTICAL + { + cur_pos -= panel_visible_dim + panel_spacing; + + if (mShowDragHandle && panel_spacing_round > mDragHandleThickness) + { + resize_bar_rect.mTop = panel_rect.mBottom - mDragHandleShift; + resize_bar_rect.mBottom = resize_bar_rect.mTop - mDragHandleThickness; + } + else + { + resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap; + resize_bar_rect.mBottom = panel_rect.mBottom - panel_spacing_round - mResizeBarOverlap; + } + + if (mShowDragHandle) + { + resize_bar_rect.mLeft += mDragHandleFirstIndent; + resize_bar_rect.mRight -= mDragHandleSecondIndent; + } + } + + panelp->setIgnoreReshape(true); + panelp->setShape(panel_rect); + panelp->setIgnoreReshape(false); + panelp->mResizeBar->setShape(resize_bar_rect); + } + + updateResizeBarLimits(); + + // clear animation flag at end, since panel resizes will set it + // and leave it set if there is any animation in progress + mNeedsLayout = continue_animating; +} // end LLLayoutStack::updateLayout + +void LLLayoutStack::setPanelSpacing(S32 val) +{ + if (mPanelSpacing != val) + { + mPanelSpacing = val; + mNeedsLayout = true; + } +} + +LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const +{ + if (!panelp) return NULL; + + for (LLLayoutPanel* p : mPanels) + { + if (p == panelp) + { + return p; + } + } + return NULL; +} + +LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const +{ + LLLayoutPanel* result = NULL; + + for (LLLayoutPanel* p : mPanels) + { + if (p->getName() == name) + { + result = p; + break; + } + } + + return result; +} + +void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) +{ + for (LLLayoutPanel* lp : mPanels) + { + if (lp->mResizeBar == NULL) + { + LLResizeBar::Params resize_params; + resize_params.name("resize"); + resize_params.resizing_view(lp); + resize_params.min_size(lp->getRelevantMinDim()); + resize_params.side((mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM); + resize_params.snapping_enabled(false); + LLResizeBar* resize_bar = LLUICtrlFactory::create(resize_params); + lp->mResizeBar = resize_bar; + + if (mShowDragHandle) + { + LLPanel::Params resize_bar_bg_panel_p; + resize_bar_bg_panel_p.name = "resize_handle_bg_panel"; + resize_bar_bg_panel_p.rect = lp->mResizeBar->getLocalRect(); + resize_bar_bg_panel_p.follows.flags = FOLLOWS_ALL; + resize_bar_bg_panel_p.tab_stop = false; + resize_bar_bg_panel_p.background_visible = true; + resize_bar_bg_panel_p.bg_alpha_color = mDragHandleColor; + resize_bar_bg_panel_p.has_border = true; + resize_bar_bg_panel_p.border.border_thickness = 1; + resize_bar_bg_panel_p.border.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); + resize_bar_bg_panel_p.border.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark"); + + LLPanel* resize_bar_bg_panel = LLUICtrlFactory::create(resize_bar_bg_panel_p); + + LLIconCtrl::Params icon_p; + icon_p.name = "resize_handle_image"; + icon_p.rect = lp->mResizeBar->getLocalRect(); + icon_p.follows.flags = FOLLOWS_ALL; + icon_p.image = LLUI::getUIImage(mOrientation == HORIZONTAL ? "Vertical Drag Handle" : "Horizontal Drag Handle"); + resize_bar_bg_panel->addChild(LLUICtrlFactory::create(icon_p)); + + lp->mResizeBar->addChild(resize_bar_bg_panel); + } + + /*if (mShowDragHandle) + { + LLViewBorder::Params border_params; + border_params.border_thickness = 1; + border_params.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); + border_params.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark"); + + addBorder(border_params); + setBorderVisible(true); + + LLImagePanel::Params image_panel; + mDragHandleImage = LLUI::getUIImage(LLResizeBar::RIGHT == mSide ? "Vertical Drag Handle" : "Horizontal Drag Handle"); + image_panel.bg_alpha_image = mDragHandleImage; + image_panel.background_visible = true; + image_panel.horizontal = (LLResizeBar::BOTTOM == mSide); + mImagePanel = LLUICtrlFactory::create(image_panel); + setImagePanel(mImagePanel); + }*/ + + //if (mShowDragHandle) + //{ + // setBackgroundVisible(true); + // setTransparentColor(LLUIColorTable::instance().getColor("ResizebarBody")); + //} + + /*if (mShowDragHandle) + { + S32 image_width = mDragHandleImage->getTextureWidth(); + S32 image_height = mDragHandleImage->getTextureHeight(); + const LLRect& panel_rect = getRect(); + S32 image_left = (panel_rect.getWidth() - image_width) / 2 - 1; + S32 image_bottom = (panel_rect.getHeight() - image_height) / 2; + mImagePanel->setRect(LLRect(image_left, image_bottom + image_height, image_left + image_width, image_bottom)); + }*/ + LLView::addChild(resize_bar, 0); + } + } + // bring all resize bars to the front so that they are clickable even over the panels + // with a bit of overlap + for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + LLResizeBar* resize_barp = (*panel_it)->mResizeBar; + sendChildToFront(resize_barp); + } +} + +// update layout stack animations, etc. once per frame +// NOTE: we use this to size world view based on animating UI, *before* we draw the UI +// we might still need to call updateLayout during UI draw phase, in case UI elements +// are resizing themselves dynamically +//static +void LLLayoutStack::updateClass() +{ + for (auto& layout : instance_snapshot()) + { + layout.updateLayout(); + layout.mAnimatedThisFrame = false; + } +} + +void LLLayoutStack::updateFractionalSizes() +{ + F32 total_resizable_dim = 0.f; + + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->mAutoResize) + { + total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); + } + } + + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->mAutoResize) + { + F32 panel_resizable_dim = llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); + panelp->mFractionalSize = panel_resizable_dim > 0.f + ? llclamp(panel_resizable_dim / total_resizable_dim, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE) + : MIN_FRACTIONAL_SIZE; + llassert(!llisnan(panelp->mFractionalSize)); + } + } + + normalizeFractionalSizes(); +} + + +void LLLayoutStack::normalizeFractionalSizes() +{ + S32 num_auto_resize_panels = 0; + F32 total_fractional_size = 0.f; + + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->mAutoResize) + { + total_fractional_size += panelp->mFractionalSize; + num_auto_resize_panels++; + } + } + + if (total_fractional_size == 0.f) + { // equal distribution + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->mAutoResize) + { + panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels; + } + } + } + else + { // renormalize + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->mAutoResize) + { + panelp->mFractionalSize /= total_fractional_size; + } + } + } +} + +bool LLLayoutStack::animatePanels() +{ + bool continue_animating = false; + + // + // animate visibility + // + for (LLLayoutPanel* panelp : mPanels) + { + if (panelp->getVisible()) + { + if (mAnimate && panelp->mVisibleAmt < 1.f) + { + if (!mAnimatedThisFrame) + { + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLSmoothInterpolation::getInterpolant(mOpenTimeConstant)); + if (panelp->mVisibleAmt > 0.99f) + { + panelp->mVisibleAmt = 1.f; + } + } + + mAnimatedThisFrame = true; + continue_animating = true; + } + else + { + if (panelp->mVisibleAmt != 1.f) + { + panelp->mVisibleAmt = 1.f; + mAnimatedThisFrame = true; + } + } + } + else // not visible + { + if (mAnimate && panelp->mVisibleAmt > 0.f) + { + if (!mAnimatedThisFrame) + { + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant)); + if (panelp->mVisibleAmt < 0.001f) + { + panelp->mVisibleAmt = 0.f; + } + } + + continue_animating = true; + mAnimatedThisFrame = true; + } + else + { + if (panelp->mVisibleAmt != 0.f) + { + panelp->mVisibleAmt = 0.f; + mAnimatedThisFrame = true; + } + } + } + + F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f; + if (panelp->mCollapseAmt != collapse_state) + { + if (mAnimate) + { + if (!mAnimatedThisFrame) + { + panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant)); + } + + if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f) + { + panelp->mCollapseAmt = collapse_state; + } + + mAnimatedThisFrame = true; + continue_animating = true; + } + else + { + panelp->mCollapseAmt = collapse_state; + mAnimatedThisFrame = true; + } + } + } + + if (mAnimatedThisFrame) mNeedsLayout = true; + return continue_animating; +} + +void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& new_rect ) +{ + S32 new_dim = (mOrientation == HORIZONTAL) + ? new_rect.getWidth() + : new_rect.getHeight(); + S32 delta_panel_dim = new_dim - resized_panel->getVisibleDim(); + if (delta_panel_dim == 0) return; + + F32 total_visible_fraction = 0.f; + F32 delta_auto_resize_headroom = 0.f; + F32 old_auto_resize_headroom = 0.f; + + LLLayoutPanel* other_resize_panel = NULL; + LLLayoutPanel* following_panel = NULL; + + BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available... + { + if (panelp->mAutoResize) + { + old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim()); + if (panelp->getVisible() && !panelp->mCollapsed) + { + total_visible_fraction += panelp->mFractionalSize; + } + } + + if (panelp == resized_panel) + { + other_resize_panel = following_panel; + } + + if (panelp->getVisible() && !panelp->mCollapsed) + { + following_panel = panelp; + } + } + + if (resized_panel->mAutoResize) + { + if (!other_resize_panel || !other_resize_panel->mAutoResize) + { + delta_auto_resize_headroom += delta_panel_dim; + } + } + else + { + if (!other_resize_panel || other_resize_panel->mAutoResize) + { + delta_auto_resize_headroom -= delta_panel_dim; + } + } + + F32 fraction_given_up = 0.f; + F32 fraction_remaining = 1.f; + F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom; + + enum + { + BEFORE_RESIZED_PANEL, + RESIZED_PANEL, + NEXT_PANEL, + AFTER_RESIZED_PANEL + } which_panel = BEFORE_RESIZED_PANEL; + + for (LLLayoutPanel* panelp : mPanels) + { + if (!panelp->getVisible() || panelp->mCollapsed) + { + if (panelp->mAutoResize) + { + fraction_remaining -= panelp->mFractionalSize; + } + continue; + } + + if (panelp == resized_panel) + { + which_panel = RESIZED_PANEL; + } + + switch(which_panel) + { + case BEFORE_RESIZED_PANEL: + if (panelp->mAutoResize) + { // freeze current size as fraction of overall auto_resize space + F32 fractional_adjustment_factor = new_auto_resize_headroom == 0.f + ? 1.f + : old_auto_resize_headroom / new_auto_resize_headroom; + F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + fraction_remaining -= panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + llassert(!llisnan(panelp->mFractionalSize)); + } + else + { + // leave non auto-resize panels alone + } + break; + case RESIZED_PANEL: + if (panelp->mAutoResize) + { // freeze new size as fraction + F32 new_fractional_size = (new_auto_resize_headroom == 0.f) + ? MAX_FRACTIONAL_SIZE + : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / new_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + fraction_remaining -= panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + llassert(!llisnan(panelp->mFractionalSize)); + } + else + { // freeze new size as original size + panelp->mTargetDim = new_dim; + } + which_panel = NEXT_PANEL; + break; + case NEXT_PANEL: + if (panelp->mAutoResize) + { + fraction_remaining -= panelp->mFractionalSize; + if (resized_panel->mAutoResize) + { + panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + fraction_given_up = 0.f; + } + else + { + if (new_auto_resize_headroom < 1.f) + { + new_auto_resize_headroom = 1.f; + } + + F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) + / new_auto_resize_headroom, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + } + } + else + { + panelp->mTargetDim -= delta_panel_dim; + } + which_panel = AFTER_RESIZED_PANEL; + break; + case AFTER_RESIZED_PANEL: + if (panelp->mAutoResize && fraction_given_up != 0.f) + { + panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + } + break; + default: + break; + } + } + updateLayout(); + //normalizeFractionalSizes(); +} + +void LLLayoutStack::reshape(S32 width, S32 height, bool called_from_parent) +{ + mNeedsLayout = true; + LLView::reshape(width, height, called_from_parent); +} + +void LLLayoutStack::updateResizeBarLimits() +{ + LLLayoutPanel* previous_visible_panelp{ nullptr }; + BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available... + { + if (!visible_panelp->getVisible() || visible_panelp->mCollapsed) + { + visible_panelp->mResizeBar->setVisible(false); + continue; + } + + // toggle resize bars based on panel visibility, resizability, etc + if (previous_visible_panelp + && (visible_panelp->mUserResize || previous_visible_panelp->mUserResize) // one of the pair is user resizable + && (visible_panelp->mAutoResize || visible_panelp->mUserResize) // current panel is resizable + && (previous_visible_panelp->mAutoResize || previous_visible_panelp->mUserResize)) // previous panel is resizable + { + visible_panelp->mResizeBar->setVisible(true); + S32 previous_panel_headroom = previous_visible_panelp->getVisibleDim() - previous_visible_panelp->getRelevantMinDim(); + visible_panelp->mResizeBar->setResizeLimits(visible_panelp->getRelevantMinDim(), + visible_panelp->getVisibleDim() + previous_panel_headroom); + } + else + { + visible_panelp->mResizeBar->setVisible(false); + } + + previous_visible_panelp = visible_panelp; + } +} + diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 080559477b..d362d4362c 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -1,216 +1,216 @@ -/** - * @file lllayoutstack.h - * @author Richard Nelson - * @brief LLLayout class - dynamic stacking of UI elements - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Reshasearch, 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_LLLAYOUTSTACK_H -#define LL_LLLAYOUTSTACK_H - -#include "llpanel.h" -#include "llresizebar.h" - - -class LLLayoutPanel; - - -class LLLayoutStack : public LLView, public LLInstanceTracker -{ -public: - - struct LayoutStackRegistry : public LLChildRegistry - { - LLSINGLETON_EMPTY_CTOR(LayoutStackRegistry); - }; - - struct Params : public LLInitParam::Block - { - Mandatory orientation; - Optional border_size; - Optional animate, - clip; - Optional open_time_constant, - close_time_constant; - Optional resize_bar_overlap; - Optional show_drag_handle; - Optional drag_handle_first_indent; - Optional drag_handle_second_indent; - Optional drag_handle_thickness; - Optional drag_handle_shift; - - Optional drag_handle_color; - - Params(); - }; - - typedef LayoutStackRegistry child_registry_t; - - virtual ~LLLayoutStack(); - - /*virtual*/ void draw(); - /*virtual*/ void deleteAllChildren(); - /*virtual*/ void removeChild(LLView*); - /*virtual*/ bool postBuild(); - /*virtual*/ bool addChild(LLView* child, S32 tab_group = 0); - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - - - static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL); - - typedef enum e_animate - { - NO_ANIMATE, - ANIMATE - } EAnimate; - - void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE); - void collapsePanel(LLPanel* panel, bool collapsed = true); - S32 getNumPanels() { return mPanels.size(); } - - void updateLayout(); - - S32 getPanelSpacing() const { return mPanelSpacing; } - void setPanelSpacing(S32 val); - - static void updateClass(); - -protected: - LLLayoutStack(const Params&); - friend class LLUICtrlFactory; - friend class LLLayoutPanel; - -private: - void updateResizeBarLimits(); - bool animatePanels(); - void createResizeBar(LLLayoutPanel* panel); - - const EOrientation mOrientation; - - typedef std::vector e_panel_list_t; - e_panel_list_t mPanels; - - LLLayoutPanel* findEmbeddedPanel(LLPanel* panelp) const; - LLLayoutPanel* findEmbeddedPanelByName(const std::string& name) const; - void updateFractionalSizes(); - void normalizeFractionalSizes(); - void updatePanelRect( LLLayoutPanel* param1, const LLRect& new_rect ); - - S32 mPanelSpacing; - - // true if we already applied animation this frame - bool mAnimatedThisFrame; - bool mAnimate; - bool mClip; - F32 mOpenTimeConstant; - F32 mCloseTimeConstant; - bool mNeedsLayout; - S32 mResizeBarOverlap; - bool mShowDragHandle; - S32 mDragHandleFirstIndent; - S32 mDragHandleSecondIndent; - S32 mDragHandleThickness; - S32 mDragHandleShift; - LLUIColor mDragHandleColor; -}; // end class LLLayoutStack - - -class LLLayoutPanel : public LLPanel -{ -friend class LLLayoutStack; -friend class LLUICtrlFactory; -public: - struct Params : public LLInitParam::Block - { - Optional expanded_min_dim, - min_dim; - Optional user_resize, - auto_resize; - - Params(); - }; - - ~LLLayoutPanel(); - - void initFromParams(const Params& p); - - void handleReshape(const LLRect& new_rect, bool by_user); - - void reshape(S32 width, S32 height, bool called_from_parent = true); - - - void setVisible(bool visible); - - S32 getLayoutDim() const; - S32 getTargetDim() const; - void setTargetDim(S32 value); - S32 getMinDim() const { return llmax(0, mMinDim); } - void setMinDim(S32 value) { mMinDim = value; } - - S32 getExpandedMinDim() const { return mExpandedMinDim >= 0 ? mExpandedMinDim : getMinDim(); } - void setExpandedMinDim(S32 value) { mExpandedMinDim = value; } - - S32 getRelevantMinDim() const - { - S32 min_dim = mMinDim; - - if (!mCollapsed) - { - min_dim = getExpandedMinDim(); - } - - return min_dim; - } - - F32 getAutoResizeFactor() const; - F32 getVisibleAmount() const; - S32 getVisibleDim() const; - LLResizeBar* getResizeBar() { return mResizeBar; } - - bool isCollapsed() const { return mCollapsed;} - - void setOrientation(LLView::EOrientation orientation); - void storeOriginalDim(); - - void setIgnoreReshape(bool ignore) { mIgnoreReshape = ignore; } - -protected: - LLLayoutPanel(const Params& p); - - const bool mAutoResize; - const bool mUserResize; - - S32 mExpandedMinDim; - S32 mMinDim; - bool mCollapsed; - F32 mVisibleAmt; - F32 mCollapseAmt; - F32 mFractionalSize; - S32 mTargetDim; - bool mIgnoreReshape; - LLView::EOrientation mOrientation; - class LLResizeBar* mResizeBar; -}; - - -#endif +/** + * @file lllayoutstack.h + * @author Richard Nelson + * @brief LLLayout class - dynamic stacking of UI elements + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Reshasearch, 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_LLLAYOUTSTACK_H +#define LL_LLLAYOUTSTACK_H + +#include "llpanel.h" +#include "llresizebar.h" + + +class LLLayoutPanel; + + +class LLLayoutStack : public LLView, public LLInstanceTracker +{ +public: + + struct LayoutStackRegistry : public LLChildRegistry + { + LLSINGLETON_EMPTY_CTOR(LayoutStackRegistry); + }; + + struct Params : public LLInitParam::Block + { + Mandatory orientation; + Optional border_size; + Optional animate, + clip; + Optional open_time_constant, + close_time_constant; + Optional resize_bar_overlap; + Optional show_drag_handle; + Optional drag_handle_first_indent; + Optional drag_handle_second_indent; + Optional drag_handle_thickness; + Optional drag_handle_shift; + + Optional drag_handle_color; + + Params(); + }; + + typedef LayoutStackRegistry child_registry_t; + + virtual ~LLLayoutStack(); + + /*virtual*/ void draw(); + /*virtual*/ void deleteAllChildren(); + /*virtual*/ void removeChild(LLView*); + /*virtual*/ bool postBuild(); + /*virtual*/ bool addChild(LLView* child, S32 tab_group = 0); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + + + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL); + + typedef enum e_animate + { + NO_ANIMATE, + ANIMATE + } EAnimate; + + void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE); + void collapsePanel(LLPanel* panel, bool collapsed = true); + S32 getNumPanels() { return mPanels.size(); } + + void updateLayout(); + + S32 getPanelSpacing() const { return mPanelSpacing; } + void setPanelSpacing(S32 val); + + static void updateClass(); + +protected: + LLLayoutStack(const Params&); + friend class LLUICtrlFactory; + friend class LLLayoutPanel; + +private: + void updateResizeBarLimits(); + bool animatePanels(); + void createResizeBar(LLLayoutPanel* panel); + + const EOrientation mOrientation; + + typedef std::vector e_panel_list_t; + e_panel_list_t mPanels; + + LLLayoutPanel* findEmbeddedPanel(LLPanel* panelp) const; + LLLayoutPanel* findEmbeddedPanelByName(const std::string& name) const; + void updateFractionalSizes(); + void normalizeFractionalSizes(); + void updatePanelRect( LLLayoutPanel* param1, const LLRect& new_rect ); + + S32 mPanelSpacing; + + // true if we already applied animation this frame + bool mAnimatedThisFrame; + bool mAnimate; + bool mClip; + F32 mOpenTimeConstant; + F32 mCloseTimeConstant; + bool mNeedsLayout; + S32 mResizeBarOverlap; + bool mShowDragHandle; + S32 mDragHandleFirstIndent; + S32 mDragHandleSecondIndent; + S32 mDragHandleThickness; + S32 mDragHandleShift; + LLUIColor mDragHandleColor; +}; // end class LLLayoutStack + + +class LLLayoutPanel : public LLPanel +{ +friend class LLLayoutStack; +friend class LLUICtrlFactory; +public: + struct Params : public LLInitParam::Block + { + Optional expanded_min_dim, + min_dim; + Optional user_resize, + auto_resize; + + Params(); + }; + + ~LLLayoutPanel(); + + void initFromParams(const Params& p); + + void handleReshape(const LLRect& new_rect, bool by_user); + + void reshape(S32 width, S32 height, bool called_from_parent = true); + + + void setVisible(bool visible); + + S32 getLayoutDim() const; + S32 getTargetDim() const; + void setTargetDim(S32 value); + S32 getMinDim() const { return llmax(0, mMinDim); } + void setMinDim(S32 value) { mMinDim = value; } + + S32 getExpandedMinDim() const { return mExpandedMinDim >= 0 ? mExpandedMinDim : getMinDim(); } + void setExpandedMinDim(S32 value) { mExpandedMinDim = value; } + + S32 getRelevantMinDim() const + { + S32 min_dim = mMinDim; + + if (!mCollapsed) + { + min_dim = getExpandedMinDim(); + } + + return min_dim; + } + + F32 getAutoResizeFactor() const; + F32 getVisibleAmount() const; + S32 getVisibleDim() const; + LLResizeBar* getResizeBar() { return mResizeBar; } + + bool isCollapsed() const { return mCollapsed;} + + void setOrientation(LLView::EOrientation orientation); + void storeOriginalDim(); + + void setIgnoreReshape(bool ignore) { mIgnoreReshape = ignore; } + +protected: + LLLayoutPanel(const Params& p); + + const bool mAutoResize; + const bool mUserResize; + + S32 mExpandedMinDim; + S32 mMinDim; + bool mCollapsed; + F32 mVisibleAmt; + F32 mCollapseAmt; + F32 mFractionalSize; + S32 mTargetDim; + bool mIgnoreReshape; + LLView::EOrientation mOrientation; + class LLResizeBar* mResizeBar; +}; + + +#endif diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 8c5e69fbb2..5ec4c34f57 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1,2735 +1,2735 @@ -/** - * @file lllineeditor.cpp - * @brief LLLineEditor base class - * - * $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$ - */ - -// Text editor widget to let users enter a single line. - -#include "linden_common.h" - -#define LLLINEEDITOR_CPP -#include "lllineeditor.h" - -#include "lltexteditor.h" -#include "llmath.h" -#include "llfontgl.h" -#include "llgl.h" -#include "lltimer.h" - -#include "llcalc.h" -//#include "llclipboard.h" -#include "llcontrol.h" -#include "llbutton.h" -#include "llfocusmgr.h" -#include "llkeyboard.h" -#include "llrect.h" -#include "llresmgr.h" -#include "llspellcheck.h" -#include "llstring.h" -#include "llwindow.h" -#include "llui.h" -#include "lluictrlfactory.h" -#include "llclipboard.h" -#include "llmenugl.h" - -// -// Imported globals -// - -// -// Constants -// - -const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds -const S32 SCROLL_INCREMENT_ADD = 0; // make space for typing -const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing -const F32 AUTO_SCROLL_TIME = 0.05f; -const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. *TODO: make this equal to the double click interval? -const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on - -const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET - -static LLDefaultChildRegistry::Register r1("line_editor"); - -// Compiler optimization, generate extern template -template class LLLineEditor* LLView::getChild( - const std::string& name, bool recurse) const; - -// -// Member functions -// - -LLLineEditor::Params::Params() -: max_length(""), - keystroke_callback("keystroke_callback"), - prevalidator("prevalidator"), - input_prevalidator("input_prevalidator"), - background_image("background_image"), - background_image_disabled("background_image_disabled"), - background_image_focused("background_image_focused"), - bg_image_always_focused("bg_image_always_focused", false), - show_label_focused("show_label_focused", false), - select_on_focus("select_on_focus", false), - revert_on_esc("revert_on_esc", true), - spellcheck("spellcheck", false), - commit_on_focus_lost("commit_on_focus_lost", true), - ignore_tab("ignore_tab", true), - is_password("is_password", false), - allow_emoji("allow_emoji", true), - cursor_color("cursor_color"), - use_bg_color("use_bg_color", false), - bg_color("bg_color"), - text_color("text_color"), - text_readonly_color("text_readonly_color"), - text_tentative_color("text_tentative_color"), - highlight_color("highlight_color"), - preedit_bg_color("preedit_bg_color"), - border(""), - bg_visible("bg_visible"), - text_pad_left("text_pad_left"), - text_pad_right("text_pad_right"), - default_text("default_text") -{ - changeDefault(mouse_opaque, true); - addSynonym(prevalidator, "prevalidate_callback"); - addSynonym(input_prevalidator, "prevalidate_input_callback"); - addSynonym(select_on_focus, "select_all_on_focus_received"); - addSynonym(border, "border"); - addSynonym(label, "watermark_text"); - addSynonym(max_length.chars, "max_length"); -} - -LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) -: LLUICtrl(p), - mMaxLengthBytes(p.max_length.bytes), - mMaxLengthChars(p.max_length.chars), - mCursorPos( 0 ), - mScrollHPos( 0 ), - mTextPadLeft(p.text_pad_left), - mTextPadRight(p.text_pad_right), - mTextLeftEdge(0), // computed in updateTextPadding() below - mTextRightEdge(0), // computed in updateTextPadding() below - mCommitOnFocusLost( p.commit_on_focus_lost ), - mKeystrokeOnEsc(false), - mRevertOnEsc( p.revert_on_esc ), - mKeystrokeCallback( p.keystroke_callback() ), - mIsSelecting( false ), - mSelectionStart( 0 ), - mSelectionEnd( 0 ), - mLastSelectionX(-1), - mLastSelectionY(-1), - mLastSelectionStart(-1), - mLastSelectionEnd(-1), - mBorderThickness( 0 ), - mIgnoreArrowKeys( false ), - mIgnoreTab( p.ignore_tab ), - mDrawAsterixes( p.is_password ), - mAllowEmoji( p.allow_emoji ), - mSpellCheck( p.spellcheck ), - mSpellCheckStart(-1), - mSpellCheckEnd(-1), - mSelectAllonFocusReceived( p.select_on_focus ), - mSelectAllonCommit( true ), - mPassDelete(false), - mReadOnly(false), - mBgImage( p.background_image ), - mBgImageDisabled( p.background_image_disabled ), - mBgImageFocused( p.background_image_focused ), - mShowImageFocused( p.bg_image_always_focused ), - mShowLabelFocused( p.show_label_focused ), - mUseBgColor(p.use_bg_color), - mHaveHistory(false), - mReplaceNewlinesWithSpaces( true ), - mPrevalidator(p.prevalidator()), - mInputPrevalidator(p.input_prevalidator()), - mLabel(p.label), - mCursorColor(p.cursor_color()), - mBgColor(p.bg_color()), - mFgColor(p.text_color()), - mReadOnlyFgColor(p.text_readonly_color()), - mTentativeFgColor(p.text_tentative_color()), - mHighlightColor(p.highlight_color()), - mPreeditBgColor(p.preedit_bg_color()), - mGLFont(p.font), - mContextMenuHandle(), - mShowContextMenu(true) -{ - llassert( mMaxLengthBytes > 0 ); - - LLUICtrl::setEnabled(true); - setEnabled(p.enabled); - - mScrollTimer.reset(); - mTripleClickTimer.reset(); - setText(p.default_text()); - - if (p.initial_value.isProvided() - && !p.control_name.isProvided()) - { - // Initial value often is descriptive, like "Type some ID here" - // and can be longer than size limitation, ignore size - setText(p.initial_value.getValue().asString(), false); - } - - // Initialize current history line iterator - mCurrentHistoryLine = mLineHistory.begin(); - - LLRect border_rect(getLocalRect()); - // adjust for gl line drawing glitch - border_rect.mTop -= 1; - border_rect.mRight -=1; - LLViewBorder::Params border_p(p.border); - border_p.rect = border_rect; - border_p.follows.flags = FOLLOWS_ALL; - border_p.bevel_style = LLViewBorder::BEVEL_IN; - mBorder = LLUICtrlFactory::create(border_p); - addChild( mBorder ); - - // clamp text padding to current editor size - updateTextPadding(); - setCursor(mText.length()); - - if (mSpellCheck) - { - LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this)); - } - mSpellCheckTimer.reset(); - - updateAllowingLanguageInput(); -} - -LLLineEditor::~LLLineEditor() -{ - mCommitOnFocusLost = false; - - // Make sure no context menu linger around once the widget is deleted - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if (menu) - { - menu->hide(); - } - setContextMenu(NULL); - - // calls onCommit() while LLLineEditor still valid - gFocusMgr.releaseFocusIfNeeded( this ); -} - -void LLLineEditor::initFromParams(const LLLineEditor::Params& params) -{ - LLUICtrl::initFromParams(params); - LLUICtrl::setEnabled(true); - setEnabled(params.enabled); -} - -void LLLineEditor::onFocusReceived() -{ - gEditMenuHandler = this; - LLUICtrl::onFocusReceived(); - updateAllowingLanguageInput(); -} - -void LLLineEditor::onFocusLost() -{ - // The call to updateAllowLanguageInput() - // when loosing the keyboard focus *may* - // indirectly invoke handleUnicodeCharHere(), - // so it must be called before onCommit. - updateAllowingLanguageInput(); - - if( mCommitOnFocusLost && mText.getString() != mPrevText) - { - onCommit(); - } - - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - getWindow()->showCursorFromMouseMove(); - - LLUICtrl::onFocusLost(); -} - -// virtual -void LLLineEditor::onCommit() -{ - // put current line into the line history - updateHistory(); - - setControlValue(getValue()); - LLUICtrl::onCommit(); - resetDirty(); - - // Selection on commit needs to be turned off when evaluating maths - // expressions, to allow indication of the error position - if (mSelectAllonCommit) selectAll(); -} - -// Returns true if user changed value at all -// virtual -bool LLLineEditor::isDirty() const -{ - return mText.getString() != mPrevText; -} - -// Clear dirty state -// virtual -void LLLineEditor::resetDirty() -{ - mPrevText = mText.getString(); -} - -// assumes UTF8 text -// virtual -void LLLineEditor::setValue(const LLSD& value ) -{ - setText(value.asString()); -} - -//virtual -LLSD LLLineEditor::getValue() const -{ - return LLSD(getText()); -} - - -// line history support -void LLLineEditor::updateHistory() -{ - // On history enabled line editors, remember committed line and - // reset current history line number. - // Be sure only to remember lines that are not empty and that are - // different from the last on the list. - if( mHaveHistory && getLength() ) - { - if( !mLineHistory.empty() ) - { - // When not empty, last line of history should always be blank. - if( mLineHistory.back().empty() ) - { - // discard the empty line - mLineHistory.pop_back(); - } - else - { - LL_WARNS("") << "Last line of history was not blank." << LL_ENDL; - } - } - - // Add text to history, ignoring duplicates - if( mLineHistory.empty() || getText() != mLineHistory.back() ) - { - mLineHistory.push_back( getText() ); - } - - // Restore the blank line and set mCurrentHistoryLine to point at it - mLineHistory.push_back( "" ); - mCurrentHistoryLine = mLineHistory.end() - 1; - } -} - -void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLUICtrl::reshape(width, height, called_from_parent); - updateTextPadding(); // For clamping side-effect. - setCursor(mCursorPos); // For clamping side-effect. -} - -void LLLineEditor::setEnabled(bool enabled) -{ - mReadOnly = !enabled; - setTabStop(!mReadOnly); - updateAllowingLanguageInput(); -} - - -void LLLineEditor::setMaxTextLength(S32 max_text_length) -{ - S32 max_len = llmax(0, max_text_length); - mMaxLengthBytes = max_len; -} - -void LLLineEditor::setMaxTextChars(S32 max_text_chars) -{ - S32 max_chars = llmax(0, max_text_chars); - mMaxLengthChars = max_chars; -} - -void LLLineEditor::getTextPadding(S32 *left, S32 *right) -{ - *left = mTextPadLeft; - *right = mTextPadRight; -} - -void LLLineEditor::setTextPadding(S32 left, S32 right) -{ - mTextPadLeft = left; - mTextPadRight = right; - updateTextPadding(); -} - -void LLLineEditor::updateTextPadding() -{ - mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth()); - mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth()); -} - - -void LLLineEditor::setText(const LLStringExplicit &new_text) -{ - setText(new_text, true); -} - -void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit) -{ - // If new text is identical, don't copy and don't move insertion point - if (mText.getString() == new_text) - { - return; - } - - // Check to see if entire field is selected. - S32 len = mText.length(); - bool all_selected = (len > 0) - && (( mSelectionStart == 0 && mSelectionEnd == len ) - || ( mSelectionStart == len && mSelectionEnd == 0 )); - - // Do safe truncation so we don't split multi-byte characters - // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor - all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived); - - std::string truncated_utf8 = new_text; - if (!mAllowEmoji) - { - // Cut emoji symbols if exist - utf8str_remove_emojis(truncated_utf8); - } - if (use_size_limit && truncated_utf8.size() > (U32)mMaxLengthBytes) - { - truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes); - } - mText.assign(truncated_utf8); - - if (use_size_limit && mMaxLengthChars) - { - mText.assign(utf8str_symbol_truncate(truncated_utf8, mMaxLengthChars)); - } - - if (all_selected) - { - // ...keep whole thing selected - selectAll(); - } - else - { - // try to preserve insertion point, but deselect text - deselect(); - } - setCursor(llmin((S32)mText.length(), getCursor())); - - // Set current history line to end of history. - if (mLineHistory.empty()) - { - mCurrentHistoryLine = mLineHistory.end(); - } - else - { - mCurrentHistoryLine = mLineHistory.end() - 1; - } - - mPrevText = mText; -} - - -// Picks a new cursor position based on the actual screen size of text being drawn. -void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x ) -{ - S32 cursor_pos = calcCursorPos(local_mouse_x); - - S32 left_pos = llmin( mSelectionStart, cursor_pos ); - S32 length = llabs( mSelectionStart - cursor_pos ); - const LLWString& substr = mText.getWString().substr(left_pos, length); - - if (mIsSelecting && !prevalidateInput(substr)) - return; - - setCursor(cursor_pos); -} - -void LLLineEditor::setCursor( S32 pos ) -{ - S32 old_cursor_pos = getCursor(); - mCursorPos = llclamp( pos, 0, mText.length()); - - // position of end of next character after cursor - S32 pixels_after_scroll = findPixelNearestPos(); - if( pixels_after_scroll > mTextRightEdge ) - { - S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos); - S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left))); - // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters) - // or first character if cursor is at beginning - S32 new_last_visible_char = llmax(0, getCursor() - 1); - S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char); - if (old_cursor_pos == last_visible_char) - { - mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD)); - } - else - { - mScrollHPos = min_scroll; - } - } - else if (getCursor() < mScrollHPos) - { - if (old_cursor_pos == mScrollHPos) - { - mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL)); - } - else - { - mScrollHPos = getCursor(); - } - } -} - - -void LLLineEditor::setCursorToEnd() -{ - setCursor(mText.length()); - deselect(); -} - -void LLLineEditor::resetScrollPosition() -{ - mScrollHPos = 0; - // make sure cursor says in visible range - setCursor(getCursor()); -} - -bool LLLineEditor::canDeselect() const -{ - return hasSelection(); -} - -void LLLineEditor::deselect() -{ - mSelectionStart = 0; - mSelectionEnd = 0; - mIsSelecting = false; -} - - -void LLLineEditor::startSelection() -{ - mIsSelecting = true; - mSelectionStart = getCursor(); - mSelectionEnd = getCursor(); -} - -void LLLineEditor::endSelection() -{ - if( mIsSelecting ) - { - mIsSelecting = false; - mSelectionEnd = getCursor(); - } -} - -bool LLLineEditor::canSelectAll() const -{ - return true; -} - -void LLLineEditor::selectAll() -{ - if (!prevalidateInput(mText.getWString())) - { - return; - } - - mSelectionStart = mText.length(); - mSelectionEnd = 0; - setCursor(mSelectionEnd); - //mScrollHPos = 0; - mIsSelecting = true; - updatePrimary(); -} - -bool LLLineEditor::getSpellCheck() const -{ - return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck); -} - -const std::string& LLLineEditor::getSuggestion(U32 index) const -{ - return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null; -} - -U32 LLLineEditor::getSuggestionCount() const -{ - return mSuggestionList.size(); -} - -void LLLineEditor::replaceWithSuggestion(U32 index) -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) - { - LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]); - if (!mAllowEmoji) - { - // Cut emoji symbols if exist - wstring_remove_emojis(suggestion); - } - if (suggestion.empty()) - return; - - deselect(); - - // Delete the misspelled word - mText.erase(it->first, it->second - it->first); - - // Insert the suggestion in its place - mText.insert(it->first, suggestion); - setCursor(it->first + (S32)suggestion.length()); - - break; - } - } - mSpellCheckStart = mSpellCheckEnd = -1; -} - -void LLLineEditor::addToDictionary() -{ - if (canAddToDictionary()) - { - LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos)); - } -} - -bool LLLineEditor::canAddToDictionary() const -{ - return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); -} - -void LLLineEditor::addToIgnore() -{ - if (canAddToIgnore()) - { - LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos)); - } -} - -bool LLLineEditor::canAddToIgnore() const -{ - return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); -} - -std::string LLLineEditor::getMisspelledWord(U32 pos) const -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= pos) && (it->second >= pos) ) - { - return wstring_to_utf8str(mText.getWString().substr(it->first, it->second - it->first)); - } - } - return LLStringUtil::null; -} - -bool LLLineEditor::isMisspelledWord(U32 pos) const -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= pos) && (it->second >= pos) ) - { - return true; - } - } - return false; -} - -void LLLineEditor::onSpellCheckSettingsChange() -{ - // Recheck the spelling on every change - mMisspellRanges.clear(); - mSpellCheckStart = mSpellCheckEnd = -1; -} - -bool LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - setFocus( true ); - mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); - - if (mSelectionEnd == 0 && mSelectionStart == mText.length()) - { - // if everything is selected, handle this as a normal click to change insertion point - handleMouseDown(x, y, mask); - } - else - { - const LLWString& wtext = mText.getWString(); - - bool doSelectAll = true; - - // Select the word we're on - if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) - { - S32 old_selection_start = mLastSelectionStart; - S32 old_selection_end = mLastSelectionEnd; - - // Select word the cursor is over - while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] )) - { // Find the start of the word - mCursorPos--; - } - startSelection(); - - while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) - { // Find the end of the word - mCursorPos++; - } - mSelectionEnd = mCursorPos; - - // If nothing changed, then the word was already selected. Select the whole line. - doSelectAll = (old_selection_start == mSelectionStart) && - (old_selection_end == mSelectionEnd); - } - - if ( doSelectAll ) - { // Select everything - selectAll(); - } - } - - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection - // here. - mIsSelecting = false; - - // delay cursor flashing - mKeystrokeTimer.reset(); - - // take selection to 'primary' clipboard - updatePrimary(); - - return true; -} - -bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // Check first whether the "clear search" button wants to deal with this. - if(childrenHandleMouseDown(x, y, mask) != NULL) - { - return true; - } - - if (!mSelectAllonFocusReceived - || gFocusMgr.getKeyboardFocus() == this) - { - mLastSelectionStart = -1; - mLastSelectionStart = -1; - - if (mask & MASK_SHIFT) - { - // assume we're starting a drag select - mIsSelecting = true; - - // Handle selection extension - S32 old_cursor_pos = getCursor(); - setCursorAtLocalPos(x); - - if (hasSelection()) - { - /* Mac-like behavior - extend selection towards the cursor - if (getCursor() < mSelectionStart - && getCursor() < mSelectionEnd) - { - // ...left of selection - mSelectionStart = llmax(mSelectionStart, mSelectionEnd); - mSelectionEnd = getCursor(); - } - else if (getCursor() > mSelectionStart - && getCursor() > mSelectionEnd) - { - // ...right of selection - mSelectionStart = llmin(mSelectionStart, mSelectionEnd); - mSelectionEnd = getCursor(); - } - else - { - mSelectionEnd = getCursor(); - } - */ - // Windows behavior - mSelectionEnd = getCursor(); - } - else - { - mSelectionStart = old_cursor_pos; - mSelectionEnd = getCursor(); - } - } - else - { - if (mTripleClickTimer.hasExpired()) - { - // Save selection for word/line selecting on double-click - mLastSelectionStart = mSelectionStart; - mLastSelectionEnd = mSelectionEnd; - - // Move cursor and deselect for regular click - setCursorAtLocalPos( x ); - deselect(); - startSelection(); - } - else // handle triple click - { - selectAll(); - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection - // here. - mIsSelecting = false; - } - } - - gFocusMgr.setMouseCapture( this ); - } - - setFocus(true); - - // delay cursor flashing - mKeystrokeTimer.reset(); - - if (mMouseDownSignal) - (*mMouseDownSignal)(this,x,y,mask); - - return true; -} - -bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - // LL_INFOS() << "MiddleMouseDown" << LL_ENDL; - setFocus( true ); - if( canPastePrimary() ) - { - setCursorAtLocalPos(x); - pastePrimary(); - } - return true; -} - -bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - setFocus(true); - if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu()) - { - showContextMenu(x, y); - } - return true; -} - -bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = false; - // Check first whether the "clear search" button wants to deal with this. - if(!hasMouseCapture()) - { - if(childrenHandleHover(x, y, mask) != NULL) - { - return true; - } - } - - if( (hasMouseCapture()) && mIsSelecting ) - { - if (x != mLastSelectionX || y != mLastSelectionY) - { - mLastSelectionX = x; - mLastSelectionY = y; - } - // Scroll if mouse cursor outside of bounds - if (mScrollTimer.hasExpired()) - { - S32 increment = ll_round(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME); - mScrollTimer.reset(); - mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME); - if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) ) - { - // Scroll to the left - mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length()); - } - else - if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) ) - { - // If scrolling one pixel would make a difference... - S32 pixels_after_scrolling_one_char = findPixelNearestPos(1); - if( pixels_after_scrolling_one_char >= mTextRightEdge ) - { - // ...scroll to the right - mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length()); - } - } - } - - setCursorAtLocalPos( x ); - mSelectionEnd = getCursor(); - - // delay cursor flashing - mKeystrokeTimer.reset(); - - getWindow()->setCursor(UI_CURSOR_IBEAM); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - handled = true; - } - - if( !handled ) - { - getWindow()->setCursor(UI_CURSOR_IBEAM); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = true; - } - - return handled; -} - - -bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - if( hasMouseCapture() ) - { - gFocusMgr.setMouseCapture( NULL ); - handled = true; - } - - // Check first whether the "clear search" button wants to deal with this. - if(!handled && childrenHandleMouseUp(x, y, mask) != NULL) - { - return true; - } - - if( mIsSelecting ) - { - setCursorAtLocalPos( x ); - mSelectionEnd = getCursor(); - - handled = true; - } - - if( handled ) - { - // delay cursor flashing - mKeystrokeTimer.reset(); - - // take selection to 'primary' clipboard - updatePrimary(); - } - - // We won't call LLUICtrl::handleMouseUp to avoid double calls of childrenHandleMouseUp().Just invoke the signal manually. - if (mMouseUpSignal) - (*mMouseUpSignal)(this,x,y, mask); - return handled; -} - - -// Remove a single character from the text -void LLLineEditor::removeChar() -{ - if( getCursor() > 0 ) - { - if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1))) - return; - - mText.erase(getCursor() - 1, 1); - - setCursor(getCursor() - 1); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } -} - -void LLLineEditor::addChar(const llwchar uni_char) -{ - if (!mAllowEmoji && LLStringOps::isEmoji(uni_char)) - return; - - llwchar new_c = uni_char; - if (hasSelection()) - { - deleteSelection(); - } - else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - if (!prevalidateInput(mText.getWString().substr(getCursor(), 1))) - return; - - mText.erase(getCursor(), 1); - } - - S32 cur_bytes = mText.getString().size(); - - S32 new_bytes = wchar_utf8_length(new_c); - - bool allow_char = true; - - // Check byte length limit - if ((new_bytes + cur_bytes) > mMaxLengthBytes) - { - allow_char = false; - } - else if (mMaxLengthChars) - { - S32 wide_chars = mText.getWString().size(); - if ((wide_chars + 1) > mMaxLengthChars) - { - allow_char = false; - } - } - - if (allow_char) - { - // Will we need to scroll? - LLWString w_buf; - w_buf.assign(1, new_c); - - mText.insert(getCursor(), w_buf); - setCursor(getCursor() + 1); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - - getWindow()->hideCursorUntilMouseMove(); -} - -// Extends the selection box to the new cursor position -void LLLineEditor::extendSelection( S32 new_cursor_pos ) -{ - if( !mIsSelecting ) - { - startSelection(); - } - - S32 left_pos = llmin( mSelectionStart, new_cursor_pos ); - S32 selection_length = llabs( mSelectionStart - new_cursor_pos ); - const LLWString& selection = mText.getWString().substr(left_pos, selection_length); - - if (!prevalidateInput(selection)) - return; - - setCursor(new_cursor_pos); - mSelectionEnd = getCursor(); -} - - -void LLLineEditor::setSelection(S32 start, S32 end) -{ - S32 len = mText.length(); - - mIsSelecting = true; - - // JC, yes, this seems odd, but I think you have to presume a - // selection dragged from the end towards the start. - mSelectionStart = llclamp(end, 0, len); - mSelectionEnd = llclamp(start, 0, len); - setCursor(start); -} - -void LLLineEditor::setDrawAsterixes(bool b) -{ - mDrawAsterixes = b; - updateAllowingLanguageInput(); -} - -S32 LLLineEditor::prevWordPos(S32 cursorPos) const -{ - const LLWString& wtext = mText.getWString(); - while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') ) - { - cursorPos--; - } - while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) - { - cursorPos--; - } - return cursorPos; -} - -S32 LLLineEditor::nextWordPos(S32 cursorPos) const -{ - const LLWString& wtext = mText.getWString(); - while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) - { - cursorPos++; - } - while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') ) - { - cursorPos++; - } - return cursorPos; -} - - -bool LLLineEditor::handleSelectionKey(KEY key, MASK mask) -{ - bool handled = false; - - if( mask & MASK_SHIFT ) - { - handled = true; - - switch( key ) - { - case KEY_LEFT: - if( 0 < getCursor() ) - { - S32 cursorPos = getCursor() - 1; - if( mask & MASK_CONTROL ) - { - cursorPos = prevWordPos(cursorPos); - } - extendSelection( cursorPos ); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - break; - - case KEY_RIGHT: - if( getCursor() < mText.length()) - { - S32 cursorPos = getCursor() + 1; - if( mask & MASK_CONTROL ) - { - cursorPos = nextWordPos(cursorPos); - } - extendSelection( cursorPos ); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - break; - - case KEY_PAGE_UP: - case KEY_HOME: - extendSelection( 0 ); - break; - - case KEY_PAGE_DOWN: - case KEY_END: - { - S32 len = mText.length(); - if( len ) - { - extendSelection( len ); - } - break; - } - - default: - handled = false; - break; - } - } - - if(handled) - { - // take selection to 'primary' clipboard - updatePrimary(); - } - - return handled; -} - -void LLLineEditor::deleteSelection() -{ - if( !mReadOnly && hasSelection() ) - { - S32 left_pos, selection_length; - getSelectionRange(&left_pos, &selection_length); - const LLWString& selection = mText.getWString().substr(left_pos, selection_length); - - if (!prevalidateInput(selection)) - return; - - mText.erase(left_pos, selection_length); - deselect(); - setCursor(left_pos); - } -} - -bool LLLineEditor::canCut() const -{ - return !mReadOnly && !mDrawAsterixes && hasSelection(); -} - -// cut selection to clipboard -void LLLineEditor::cut() -{ - if( canCut() ) - { - S32 left_pos, length; - getSelectionRange(&left_pos, &length); - const LLWString& selection = mText.getWString().substr(left_pos, length); - - if (!prevalidateInput(selection)) - return; - - // Prepare for possible rollback - LLLineEditorRollback rollback( this ); - - LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); - deleteSelection(); - - // Validate new string and rollback the if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback( this ); - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - else - { - onKeystroke(); - } - } -} - -bool LLLineEditor::canCopy() const -{ - return !mDrawAsterixes && hasSelection(); -} - - -// copy selection to clipboard -void LLLineEditor::copy() -{ - if( canCopy() ) - { - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); - } -} - -bool LLLineEditor::canPaste() const -{ - return !mReadOnly && LLClipboard::instance().isTextAvailable(); -} - -void LLLineEditor::paste() -{ - bool is_primary = false; - pasteHelper(is_primary); -} - -void LLLineEditor::pastePrimary() -{ - bool is_primary = true; - pasteHelper(is_primary); -} - -// paste from primary (is_primary==true) or clipboard (is_primary==false) -void LLLineEditor::pasteHelper(bool is_primary) -{ - bool can_paste_it; - if (is_primary) - { - can_paste_it = canPastePrimary(); - } - else - { - can_paste_it = canPaste(); - } - - if (can_paste_it) - { - LLWString paste; - LLClipboard::instance().pasteFromClipboard(paste, is_primary); - - if (!paste.empty()) - { - if (!mAllowEmoji) - { - wstring_remove_emojis(paste); - } - - if (!prevalidateInput(paste)) - return; - - // Prepare for possible rollback - LLLineEditorRollback rollback(this); - - // Delete any selected characters - if ((!is_primary) && hasSelection()) - { - deleteSelection(); - } - - // Clean up string (replace tabs and returns and remove characters that our fonts don't support.) - LLWString clean_string(paste); - LLWStringUtil::replaceTabsWithSpaces(clean_string, 1); - //clean_string = wstring_detabify(paste, 1); - LLWStringUtil::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182); // 182 == paragraph character - - // Insert the string - - // Check to see that the size isn't going to be larger than the max number of bytes - U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText); - - if ( available_bytes < (U32) wstring_utf8_length(clean_string) ) - { // Doesn't all fit - llwchar current_symbol = clean_string[0]; - U32 wchars_that_fit = 0; - U32 total_bytes = wchar_utf8_length(current_symbol); - - //loop over the "wide" characters (symbols) - //and check to see how large (in bytes) each symbol is. - while ( total_bytes <= available_bytes ) - { - //while we still have available bytes - //"accept" the current symbol and check the size - //of the next one - current_symbol = clean_string[++wchars_that_fit]; - total_bytes += wchar_utf8_length(current_symbol); - } - // Truncate the clean string at the limit of what will fit - clean_string = clean_string.substr(0, wchars_that_fit); - LLUI::getInstance()->reportBadKeystroke(); - } - - if (mMaxLengthChars) - { - U32 available_chars = mMaxLengthChars - mText.getWString().size(); - - if (available_chars < clean_string.size()) - { - clean_string = clean_string.substr(0, available_chars); - } - - LLUI::getInstance()->reportBadKeystroke(); - } - - mText.insert(getCursor(), clean_string); - setCursor( getCursor() + (S32)clean_string.length() ); - deselect(); - - // Validate new string and rollback the if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback( this ); - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - else - { - onKeystroke(); - } - } - } -} - -// copy selection to primary -void LLLineEditor::copyPrimary() -{ - if( canCopy() ) - { - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true); - } -} - -bool LLLineEditor::canPastePrimary() const -{ - return !mReadOnly && LLClipboard::instance().isTextAvailable(true); -} - -void LLLineEditor::updatePrimary() -{ - if(canCopy() ) - { - copyPrimary(); - } -} - -bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) -{ - bool handled = false; - - switch( key ) - { - case KEY_INSERT: - if (mask == MASK_NONE) - { - gKeyboard->toggleInsertMode(); - } - - handled = true; - break; - - case KEY_BACKSPACE: - if (!mReadOnly) - { - //LL_INFOS() << "Handling backspace" << LL_ENDL; - if( hasSelection() ) - { - deleteSelection(); - } - else - if( 0 < getCursor() ) - { - removeChar(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - } - handled = true; - break; - - case KEY_PAGE_UP: - case KEY_HOME: - if (!mIgnoreArrowKeys) - { - setCursor(0); - handled = true; - } - break; - - case KEY_PAGE_DOWN: - case KEY_END: - if (!mIgnoreArrowKeys) - { - S32 len = mText.length(); - if( len ) - { - setCursor(len); - } - handled = true; - } - break; - - case KEY_LEFT: - if (mIgnoreArrowKeys && mask == MASK_NONE) - break; - if ((mask & MASK_ALT) == 0) - { - if( hasSelection() ) - { - setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd )); - } - else - if( 0 < getCursor() ) - { - S32 cursorPos = getCursor() - 1; - if( mask & MASK_CONTROL ) - { - cursorPos = prevWordPos(cursorPos); - } - setCursor(cursorPos); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - case KEY_RIGHT: - if (mIgnoreArrowKeys && mask == MASK_NONE) - break; - if ((mask & MASK_ALT) == 0) - { - if (hasSelection()) - { - setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd)); - } - else - if (getCursor() < mText.length()) - { - S32 cursorPos = getCursor() + 1; - if( mask & MASK_CONTROL ) - { - cursorPos = nextWordPos(cursorPos); - } - setCursor(cursorPos); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - // handle ctrl-uparrow if we have a history enabled line editor. - case KEY_UP: - if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) - { - if (mCurrentHistoryLine > mLineHistory.begin()) - { - mText.assign(*(--mCurrentHistoryLine)); - setCursorToEnd(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - // handle [ctrl]-downarrow if we have a history enabled line editor - case KEY_DOWN: - if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) - { - if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1) - { - mText.assign( *(++mCurrentHistoryLine) ); - setCursorToEnd(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - case KEY_RETURN: - // store sent line in history - updateHistory(); - break; - - case KEY_ESCAPE: - if (mRevertOnEsc && mText.getString() != mPrevText) - { - setText(mPrevText); - // Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged) - if (mKeystrokeOnEsc) - { - onKeystroke(); - } - } - break; - - default: - break; - } - - return handled; -} - - -bool LLLineEditor::handleKeyHere(KEY key, MASK mask ) -{ - bool handled = false; - bool selection_modified = false; - - if ( gFocusMgr.getKeyboardFocus() == this ) - { - LLLineEditorRollback rollback( this ); - - if( !handled ) - { - handled = handleSelectionKey( key, mask ); - selection_modified = handled; - } - - // Handle most keys only if the text editor is writeable. - if ( !mReadOnly ) - { - if( !handled ) - { - handled = handleSpecialKey( key, mask ); - } - } - - if( handled ) - { - mKeystrokeTimer.reset(); - - // Most keystrokes will make the selection box go away, but not all will. - if( !selection_modified && - KEY_SHIFT != key && - KEY_CONTROL != key && - KEY_ALT != key && - KEY_CAPSLOCK != key) - { - deselect(); - } - - bool prevalidator_failed = false; - - // If read-only, don't allow changes - bool need_to_rollback = mReadOnly && (mText.getString() == rollback.getText()); - - // Validate new string and rollback the keystroke if needed. - if (!need_to_rollback && mPrevalidator) - { - prevalidator_failed = !mPrevalidator.validate(mText.getWString()); - need_to_rollback |= prevalidator_failed; - } - - if (need_to_rollback) - { - rollback.doRollback(this); - - LLUI::getInstance()->reportBadKeystroke(); - if (prevalidator_failed) - { - mPrevalidator.showLastErrorUsingTimeout(); - } - } - - // Notify owner if requested - if (!need_to_rollback && handled) - { - onKeystroke(); - if ( (!selection_modified) && (KEY_BACKSPACE == key) ) - { - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); - } - } - } - } - - return handled; -} - - -bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return false; - } - - bool handled = false; - - if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly) - { - handled = true; - - LLLineEditorRollback rollback( this ); - - { - LLWString u_char; - u_char.assign(1, uni_char); - if (!prevalidateInput(u_char)) - return handled; - } - - addChar(uni_char); - - mKeystrokeTimer.reset(); - - deselect(); - - // Validate new string and rollback the keystroke if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback( this ); - - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - - // Notify owner if requested - if (!need_to_rollback && handled) - { - // HACK! The only usage of this callback doesn't do anything with the character. - // We'll have to do something about this if something ever changes! - Doug - onKeystroke(); - - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); - } - } - return handled; -} - - -bool LLLineEditor::canDoDelete() const -{ - return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) ); -} - -void LLLineEditor::doDelete() -{ - if (canDoDelete() && mText.length() > 0) - { - // Prepare for possible rollback - LLLineEditorRollback rollback( this ); - - if (hasSelection()) - { - deleteSelection(); - } - else if ( getCursor() < mText.length()) - { - const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1); - - if (!prevalidateInput(text_to_delete)) - { - onKeystroke(); - return; - } - setCursor(getCursor() + 1); - removeChar(); - } - - // Validate new string and rollback the if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback(this); - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - else - { - onKeystroke(); - - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); - } - } -} - - -void LLLineEditor::drawBackground() -{ - F32 alpha = getCurrentTransparency(); - if (mUseBgColor) - { - gl_rect_2d(getLocalRect(), mBgColor % alpha, true); - } - else - { - bool has_focus = hasFocus(); - LLUIImage* image; - if (mReadOnly) - { - image = mBgImageDisabled; - } - else if (has_focus || mShowImageFocused) - { - image = mBgImageFocused; - } - else - { - image = mBgImage; - } - - if (!image) return; - // optionally draw programmatic border - if (has_focus) - { - LLColor4 tmp_color = gFocusMgr.getFocusColor(); - tmp_color.setAlpha(alpha); - image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(), - tmp_color, - gFocusMgr.getFocusFlashWidth()); - } - LLColor4 tmp_color = UI_VERTEX_COLOR; - tmp_color.setAlpha(alpha); - image->draw(getLocalRect(), tmp_color); - } -} - -//virtual -void LLLineEditor::draw() -{ - F32 alpha = getDrawContext().mAlpha; - S32 text_len = mText.length(); - static LLUICachedControl lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0); - static LLUICachedControl preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); - static LLUICachedControl preedit_marker_gap ("UIPreeditMarkerGap", 0); - static LLUICachedControl preedit_marker_position ("UIPreeditMarkerPosition", 0); - static LLUICachedControl preedit_marker_thickness ("UIPreeditMarkerThickness", 0); - static LLUICachedControl preedit_standout_brightness ("UIPreeditStandoutBrightness", 0); - static LLUICachedControl preedit_standout_gap ("UIPreeditStandoutGap", 0); - static LLUICachedControl preedit_standout_position ("UIPreeditStandoutPosition", 0); - static LLUICachedControl preedit_standout_thickness ("UIPreeditStandoutThickness", 0); - - std::string saved_text; - if (mDrawAsterixes) - { - saved_text = mText.getString(); - std::string text; - for (S32 i = 0; i < mText.length(); i++) - { - text += PASSWORD_ASTERISK; - } - mText = text; - } - - // draw rectangle for the background - LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - background.stretch( -mBorderThickness ); - - S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2; - if (mSpellCheck) - { - lineeditor_v_pad += 1; - } - - drawBackground(); - - // draw text - - // With viewer-2 art files, input region is 2 pixels up - S32 cursor_bottom = background.mBottom + 2; - S32 cursor_top = background.mTop - 1; - - LLColor4 text_color; - if (!mReadOnly) - { - if (!getTentative()) - { - text_color = mFgColor.get(); - } - else - { - text_color = mTentativeFgColor.get(); - } - } - else - { - text_color = mReadOnlyFgColor.get(); - } - text_color.setAlpha(alpha); - LLColor4 label_color = mTentativeFgColor.get(); - label_color.setAlpha(alpha); - - if (hasPreeditString()) - { - // Draw preedit markers. This needs to be before drawing letters. - for (U32 i = 0; i < mPreeditStandouts.size(); i++) - { - const S32 preedit_left = mPreeditPositions[i]; - const S32 preedit_right = mPreeditPositions[i + 1]; - if (preedit_right > mScrollHPos) - { - S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor()); - S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight); - if (preedit_pixels_left >= background.mRight) - { - break; - } - if (mPreeditStandouts[i]) - { - gl_rect_2d(preedit_pixels_left + preedit_standout_gap, - background.mBottom + preedit_standout_position, - preedit_pixels_right - preedit_standout_gap - 1, - background.mBottom + preedit_standout_position - preedit_standout_thickness, - (text_color * preedit_standout_brightness - + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/)); - } - else - { - gl_rect_2d(preedit_pixels_left + preedit_marker_gap, - background.mBottom + preedit_marker_position, - preedit_pixels_right - preedit_marker_gap - 1, - background.mBottom + preedit_marker_position - preedit_marker_thickness, - (text_color * preedit_marker_brightness - + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/)); - } - } - } - } - - S32 rendered_text = 0; - F32 rendered_pixels_right = (F32)mTextLeftEdge; - F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad; - - if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() ) - { - S32 select_left; - S32 select_right; - if (mSelectionStart < mSelectionEnd) - { - select_left = mSelectionStart; - select_right = mSelectionEnd; - } - else - { - select_left = mSelectionEnd; - select_right = mSelectionStart; - } - - if( select_left > mScrollHPos ) - { - // unselected, left side - rendered_text = mGLFont->render( - mText, mScrollHPos, - rendered_pixels_right, text_bottom, - text_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - select_left - mScrollHPos, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - - if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) - { - LLColor4 color = mHighlightColor; - color.setAlpha(alpha); - // selected middle - S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); - width = llmin(width, mTextRightEdge - ll_round(rendered_pixels_right)); - gl_rect_2d(ll_round(rendered_pixels_right), cursor_top, ll_round(rendered_pixels_right)+width, cursor_bottom, color); - - LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); - rendered_text += mGLFont->render( - mText, mScrollHPos + rendered_text, - rendered_pixels_right, text_bottom, - tmp_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - select_right - mScrollHPos - rendered_text, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - - if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) - { - // unselected, right side - rendered_text += mGLFont->render( - mText, mScrollHPos + rendered_text, - rendered_pixels_right, text_bottom, - text_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - } - else - { - rendered_text = mGLFont->render( - mText, mScrollHPos, - rendered_pixels_right, text_bottom, - text_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } -#if 1 // for when we're ready for image art. - mBorder->setVisible(false); // no more programmatic art. -#endif - - if ( (getSpellCheck()) && (mText.length() > 2) ) - { - // Calculate start and end indices for the first and last visible word - U32 start = prevWordPos(mScrollHPos), end = nextWordPos(mScrollHPos + rendered_text); - - if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) ) - { - const LLWString& text = mText.getWString().substr(start, end); - - // Find the start of the first word - U32 word_start = 0, word_end = 0; - while ( (word_start < text.length()) && (!LLStringOps::isAlpha(text[word_start])) ) - { - word_start++; - } - - // Iterate over all words in the text block and check them one by one - mMisspellRanges.clear(); - while (word_start < text.length()) - { - // Find the end of the current word (special case handling for "'" when it's used as a contraction) - word_end = word_start + 1; - while ( (word_end < text.length()) && - ((LLWStringUtil::isPartOfWord(text[word_end])) || - ((L'\'' == text[word_end]) && (word_end + 1 < text.length()) && - (LLStringOps::isAlnum(text[word_end - 1])) && (LLStringOps::isAlnum(text[word_end + 1])))) ) - { - word_end++; - } - if (word_end > text.length()) - { - break; - } - - // Don't process words shorter than 3 characters - std::string word = wstring_to_utf8str(text.substr(word_start, word_end - word_start)); - if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) - { - mMisspellRanges.push_back(std::pair(start + word_start, start + word_end)); - } - - // Find the start of the next word - word_start = word_end + 1; - while ( (word_start < text.length()) && (!LLWStringUtil::isPartOfWord(text[word_start])) ) - { - word_start++; - } - } - - mSpellCheckStart = start; - mSpellCheckEnd = end; - } - - // Draw squiggly lines under any (visible) misspelled words - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - // Skip over words that aren't (partially) visible - if ( ((it->first < start) && (it->second < start)) || (it->first > end) ) - { - continue; - } - - // Skip the current word if the user is still busy editing it - if ( (!mSpellCheckTimer.hasExpired()) && (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) - { - continue; - } - - S32 pxWidth = getRect().getWidth(); - S32 pxStart = findPixelNearestPos(it->first - getCursor()); - if (pxStart > pxWidth) - { - continue; - } - S32 pxEnd = findPixelNearestPos(it->second - getCursor()); - if (pxEnd > pxWidth) - { - pxEnd = pxWidth; - } - - S32 pxBottom = (S32)(text_bottom + mGLFont->getDescenderHeight()); - - gGL.color4ub(255, 0, 0, 200); - while (pxStart + 1 < pxEnd) - { - gl_line_2d(pxStart, pxBottom, pxStart + 2, pxBottom - 2); - if (pxStart + 3 < pxEnd) - { - gl_line_2d(pxStart + 2, pxBottom - 3, pxStart + 4, pxBottom - 1); - } - pxStart += 4; - } - } - } - - // If we're editing... - if( hasFocus()) - { - //mBorder->setVisible(true); // ok, programmer art just this once. - // (Flash the cursor every half second) - if (!mReadOnly && gFocusMgr.getAppHasFocus()) - { - F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); - if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) - { - S32 cursor_left = findPixelNearestPos(); - cursor_left -= lineeditor_cursor_thickness / 2; - S32 cursor_right = cursor_left + lineeditor_cursor_thickness; - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) - { - const LLWString space(utf8str_to_wstring(std::string(" "))); - S32 wswidth = mGLFont->getWidth(space.c_str()); - S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1; - cursor_right = cursor_left + llmax(wswidth, width); - } - // Use same color as text for the Cursor - gl_rect_2d(cursor_left, cursor_top, - cursor_right, cursor_bottom, text_color); - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) - { - LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); - mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom, - tmp_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - 1); - } - - // Make sure the IME is in the right place - S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position - LLRect screen_pos = calcScreenRect(); - LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - lineeditor_v_pad ); - - ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]); - ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]); - getWindow()->setLanguageTextInput( ime_pos ); - } - } - - //draw label if no text is provided - //but we should draw it in a different color - //to give indication that it is not text you typed in - if (0 == mText.length() && (mReadOnly || mShowLabelFocused)) - { - mGLFont->render(mLabel.getWString(), 0, - mTextLeftEdge, (F32)text_bottom, - label_color, - LLFontGL::LEFT, - LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right, false); - } - - - // Draw children (border) - //mBorder->setVisible(true); - mBorder->setKeyboardFocusHighlight( true ); - LLView::draw(); - mBorder->setKeyboardFocusHighlight( false ); - //mBorder->setVisible(false); - } - else // does not have keyboard input - { - // draw label if no text provided - if (0 == mText.length()) - { - mGLFont->render(mLabel.getWString(), 0, - mTextLeftEdge, (F32)text_bottom, - label_color, - LLFontGL::LEFT, - LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - // Draw children (border) - LLView::draw(); - } - - if (mDrawAsterixes) - { - mText = saved_text; - } -} - - -// Returns the local screen space X coordinate associated with the text cursor position. -S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const -{ - S32 dpos = getCursor() - mScrollHPos + cursor_offset; - S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge; - return result; -} - -S32 LLLineEditor::calcCursorPos(S32 mouse_x) -{ - const llwchar* wtext = mText.getWString().c_str(); - LLWString asterix_text; - if (mDrawAsterixes) - { - for (S32 i = 0; i < mText.length(); i++) - { - asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK); - } - wtext = asterix_text.c_str(); - } - - S32 cur_pos = mScrollHPos + - mGLFont->charFromPixelOffset( - wtext, mScrollHPos, - (F32)(mouse_x - mTextLeftEdge), - (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive - - return cur_pos; -} -//virtual -void LLLineEditor::clear() -{ - mText.clear(); - setCursor(0); -} - -//virtual -void LLLineEditor::onTabInto() -{ - selectAll(); - LLUICtrl::onTabInto(); -} - -//virtual -bool LLLineEditor::acceptsTextInput() const -{ - return true; -} - -// Start or stop the editor from accepting text-editing keystrokes -void LLLineEditor::setFocus( bool new_state ) -{ - bool old_state = hasFocus(); - - if (!new_state) - { - getWindow()->allowLanguageTextInput(this, false); - } - - - // getting focus when we didn't have it before, and we want to select all - if (!old_state && new_state && mSelectAllonFocusReceived) - { - selectAll(); - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection - // here. - mIsSelecting = false; - } - - if( new_state ) - { - gEditMenuHandler = this; - - // Don't start the cursor flashing right away - mKeystrokeTimer.reset(); - } - else - { - // Not really needed, since loss of keyboard focus should take care of this, - // but limited paranoia is ok. - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - endSelection(); - } - - LLUICtrl::setFocus( new_state ); - - if (new_state) - { - // Allow Language Text Input only when this LineEditor has - // no prevalidate function attached. This criterion works - // fine on 1.15.0.2, since all prevalidate func reject any - // non-ASCII characters. I'm not sure on future versions, - // however. - getWindow()->allowLanguageTextInput(this, !mPrevalidator); - } -} - -//virtual -void LLLineEditor::setRect(const LLRect& rect) -{ - LLUICtrl::setRect(rect); - if (mBorder) - { - LLRect border_rect = mBorder->getRect(); - // Scalable UI somehow made these rectangles off-by-one. - // I don't know why. JC - border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom, - rect.getWidth()-1, rect.getHeight()-1); - mBorder->setRect(border_rect); - } -} - -void LLLineEditor::setPrevalidate(LLTextValidate::Validator validator) -{ - mPrevalidator = validator; - updateAllowingLanguageInput(); -} - -void LLLineEditor::setPrevalidateInput(LLTextValidate::Validator validator) -{ - mInputPrevalidator = validator; - updateAllowingLanguageInput(); -} - -bool LLLineEditor::prevalidateInput(const LLWString& wstr) -{ - return mInputPrevalidator.validate(wstr); -} - -// static -bool LLLineEditor::postvalidateFloat(const std::string &str) -{ - LLLocale locale(LLLocale::USER_LOCALE); - - bool success = true; - bool has_decimal = false; - bool has_digit = false; - - LLWString trimmed = utf8str_to_wstring(str); - LLWStringUtil::trim(trimmed); - S32 len = trimmed.length(); - if( 0 < len ) - { - S32 i = 0; - - // First character can be a negative sign - if( '-' == trimmed[0] ) - { - i++; - } - - // May be a comma or period, depending on the locale - llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint(); - - for( ; i < len; i++ ) - { - if( decimal_point == trimmed[i] ) - { - if( has_decimal ) - { - // can't have two - success = false; - break; - } - else - { - has_decimal = true; - } - } - else - if( LLStringOps::isDigit( trimmed[i] ) ) - { - has_digit = true; - } - else - { - success = false; - break; - } - } - } - - // Gotta have at least one - success = has_digit; - - return success; -} - -bool LLLineEditor::evaluateFloat() -{ - bool success; - F32 result = 0.f; - std::string expr = getText(); - LLStringUtil::toUpper(expr); - - success = LLCalc::getInstance()->evalString(expr, result); - - if (!success) - { - // Move the cursor to near the error on failure - setCursor(LLCalc::getInstance()->getLastErrorPos()); - // *TODO: Translated error message indicating the type of error? Select error text? - } - else - { - // Replace the expression with the result - std::string result_str = llformat("%f",result); - setText(result_str); - selectAll(); - } - - return success; -} - -void LLLineEditor::onMouseCaptureLost() -{ - endSelection(); -} - - -void LLLineEditor::setSelectAllonFocusReceived(bool b) -{ - mSelectAllonFocusReceived = b; -} - -void LLLineEditor::onKeystroke() -{ - if (mKeystrokeCallback) - { - mKeystrokeCallback(this); - } - - mSpellCheckStart = mSpellCheckEnd = -1; -} - -void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data) -{ - mKeystrokeCallback = boost::bind(callback, _1, user_data); -} - - -bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) -{ - mText.setArg(key, text); - return true; -} - -bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - mLabel.setArg(key, text); - return true; -} - - -void LLLineEditor::updateAllowingLanguageInput() -{ - // Allow Language Text Input only when this LineEditor has - // no prevalidate function attached (as long as other criteria - // common to LLTextEditor). This criterion works - // fine on 1.15.0.2, since all prevalidate func reject any - // non-ASCII characters. I'm not sure on future versions, - // however... - LLWindow* window = getWindow(); - if (!window) - { - // test app, no window available - return; - } - if (hasFocus() && !mReadOnly && !mDrawAsterixes && !mPrevalidator) - { - window->allowLanguageTextInput(this, true); - } - else - { - window->allowLanguageTextInput(this, false); - } -} - -bool LLLineEditor::hasPreeditString() const -{ - return (mPreeditPositions.size() > 1); -} - -void LLLineEditor::resetPreedit() -{ - if (hasSelection()) - { - if (hasPreeditString()) - { - LL_WARNS() << "Preedit and selection!" << LL_ENDL; - deselect(); - } - else - { - deleteSelection(); - } - } - if (hasPreeditString()) - { - const S32 preedit_pos = mPreeditPositions.front(); - mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos); - mText.insert(preedit_pos, mPreeditOverwrittenWString); - setCursor(preedit_pos); - - mPreeditWString.clear(); - mPreeditOverwrittenWString.clear(); - mPreeditPositions.clear(); - - // Don't reset key stroke timer nor invoke keystroke callback, - // because a call to updatePreedit should be follow soon in - // normal course of operation, and timer and callback will be - // maintained there. Doing so here made an odd sound. (VWR-3410) - } -} - -void LLLineEditor::updatePreedit(const LLWString &preedit_string, - const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) -{ - // Just in case. - if (mReadOnly) - { - return; - } - - // Note that call to updatePreedit is always preceeded by resetPreedit, - // so we have no existing selection/preedit. - - S32 insert_preedit_at = getCursor(); - - mPreeditWString = preedit_string; - mPreeditPositions.resize(preedit_segment_lengths.size() + 1); - S32 position = insert_preedit_at; - for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) - { - mPreeditPositions[i] = position; - position += preedit_segment_lengths[i]; - } - mPreeditPositions.back() = position; - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) ); - mText.erase(insert_preedit_at, mPreeditWString.length()); - } - else - { - mPreeditOverwrittenWString.clear(); - } - mText.insert(insert_preedit_at, mPreeditWString); - - mPreeditStandouts = preedit_standouts; - - setCursor(position); - setCursor(mPreeditPositions.front() + caret_position); - - // Update of the preedit should be caused by some key strokes. - mKeystrokeTimer.reset(); - onKeystroke(); - - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); -} - -bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const -{ - if (control) - { - LLRect control_rect_screen; - localRectToScreen(getRect(), &control_rect_screen); - LLUI::getInstance()->screenRectToGL(control_rect_screen, control); - } - - S32 preedit_left_column, preedit_right_column; - if (hasPreeditString()) - { - preedit_left_column = mPreeditPositions.front(); - preedit_right_column = mPreeditPositions.back(); - } - else - { - preedit_left_column = preedit_right_column = getCursor(); - } - if (preedit_right_column < mScrollHPos) - { - // This should not occure... - return false; - } - - const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor()); - if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column) - { - return false; - } - - if (coord) - { - S32 query_local = findPixelNearestPos(query - getCursor()); - S32 query_screen_x, query_screen_y; - localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y); - LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); - } - - if (bounds) - { - S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor()); - S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness); - if (preedit_left_local > preedit_right_local) - { - // Is this condition possible? - preedit_right_local = preedit_left_local; - } - - LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0); - LLRect preedit_rect_screen; - localRectToScreen(preedit_rect_local, &preedit_rect_screen); - LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); - } - - return true; -} - -void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const -{ - if (hasPreeditString()) - { - *position = mPreeditPositions.front(); - *length = mPreeditPositions.back() - mPreeditPositions.front(); - } - else - { - *position = mCursorPos; - *length = 0; - } -} - -void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const -{ - if (hasSelection()) - { - *position = llmin(mSelectionStart, mSelectionEnd); - *length = llabs(mSelectionStart - mSelectionEnd); - } - else - { - *position = mCursorPos; - *length = 0; - } -} - -void LLLineEditor::markAsPreedit(S32 position, S32 length) -{ - deselect(); - setCursor(position); - if (hasPreeditString()) - { - LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL; - } - mPreeditWString.assign( LLWString( mText.getWString(), position, length ) ); - if (length > 0) - { - mPreeditPositions.resize(2); - mPreeditPositions[0] = position; - mPreeditPositions[1] = position + length; - mPreeditStandouts.resize(1); - mPreeditStandouts[0] = false; - } - else - { - mPreeditPositions.clear(); - mPreeditStandouts.clear(); - } - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - mPreeditOverwrittenWString = mPreeditWString; - } - else - { - mPreeditOverwrittenWString.clear(); - } -} - -S32 LLLineEditor::getPreeditFontSize() const -{ - return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); -} - -void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace) -{ - mReplaceNewlinesWithSpaces = replace; -} - -LLWString LLLineEditor::getConvertedText() const -{ - LLWString text = getWText(); - LLWStringUtil::trim(text); - if (!mReplaceNewlinesWithSpaces) - { - LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. - } - return text; -} - -void LLLineEditor::showContextMenu(S32 x, S32 y) -{ - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if (!menu) - { - llassert(LLMenuGL::sMenuContainer != NULL); - menu = LLUICtrlFactory::createFromFile - ("menu_text_editor.xml", - LLMenuGL::sMenuContainer, - LLMenuHolderGL::child_registry_t::instance()); - setContextMenu(menu); - } - - if (menu) - { - gEditMenuHandler = this; - - S32 screen_x, screen_y; - localPointToScreen(x, y, &screen_x, &screen_y); - - setCursorAtLocalPos(x); - if (hasSelection()) - { - if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) ) - { - deselect(); - } - else - { - setCursor(llmax(mSelectionStart, mSelectionEnd)); - } - } - - bool use_spellcheck = getSpellCheck(), is_misspelled = false; - if (use_spellcheck) - { - mSuggestionList.clear(); - - // If the cursor is on a misspelled word, retrieve suggestions for it - std::string misspelled_word = getMisspelledWord(mCursorPos); - if ((is_misspelled = !misspelled_word.empty())) - { - LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); - } - } - - menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty())); - menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled)); - menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled)); - menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled)); - menu->show(screen_x, screen_y, this); - } -} - -void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu) -{ - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if (menu) - { - menu->die(); - mContextMenuHandle.markDead(); - } - - if (new_context_menu) - { - mContextMenuHandle = new_context_menu->getHandle(); - } -} - -void LLLineEditor::setFont(const LLFontGL* font) -{ - mGLFont = font; -} +/** + * @file lllineeditor.cpp + * @brief LLLineEditor base class + * + * $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$ + */ + +// Text editor widget to let users enter a single line. + +#include "linden_common.h" + +#define LLLINEEDITOR_CPP +#include "lllineeditor.h" + +#include "lltexteditor.h" +#include "llmath.h" +#include "llfontgl.h" +#include "llgl.h" +#include "lltimer.h" + +#include "llcalc.h" +//#include "llclipboard.h" +#include "llcontrol.h" +#include "llbutton.h" +#include "llfocusmgr.h" +#include "llkeyboard.h" +#include "llrect.h" +#include "llresmgr.h" +#include "llspellcheck.h" +#include "llstring.h" +#include "llwindow.h" +#include "llui.h" +#include "lluictrlfactory.h" +#include "llclipboard.h" +#include "llmenugl.h" + +// +// Imported globals +// + +// +// Constants +// + +const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds +const S32 SCROLL_INCREMENT_ADD = 0; // make space for typing +const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing +const F32 AUTO_SCROLL_TIME = 0.05f; +const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. *TODO: make this equal to the double click interval? +const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on + +const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET + +static LLDefaultChildRegistry::Register r1("line_editor"); + +// Compiler optimization, generate extern template +template class LLLineEditor* LLView::getChild( + const std::string& name, bool recurse) const; + +// +// Member functions +// + +LLLineEditor::Params::Params() +: max_length(""), + keystroke_callback("keystroke_callback"), + prevalidator("prevalidator"), + input_prevalidator("input_prevalidator"), + background_image("background_image"), + background_image_disabled("background_image_disabled"), + background_image_focused("background_image_focused"), + bg_image_always_focused("bg_image_always_focused", false), + show_label_focused("show_label_focused", false), + select_on_focus("select_on_focus", false), + revert_on_esc("revert_on_esc", true), + spellcheck("spellcheck", false), + commit_on_focus_lost("commit_on_focus_lost", true), + ignore_tab("ignore_tab", true), + is_password("is_password", false), + allow_emoji("allow_emoji", true), + cursor_color("cursor_color"), + use_bg_color("use_bg_color", false), + bg_color("bg_color"), + text_color("text_color"), + text_readonly_color("text_readonly_color"), + text_tentative_color("text_tentative_color"), + highlight_color("highlight_color"), + preedit_bg_color("preedit_bg_color"), + border(""), + bg_visible("bg_visible"), + text_pad_left("text_pad_left"), + text_pad_right("text_pad_right"), + default_text("default_text") +{ + changeDefault(mouse_opaque, true); + addSynonym(prevalidator, "prevalidate_callback"); + addSynonym(input_prevalidator, "prevalidate_input_callback"); + addSynonym(select_on_focus, "select_all_on_focus_received"); + addSynonym(border, "border"); + addSynonym(label, "watermark_text"); + addSynonym(max_length.chars, "max_length"); +} + +LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) +: LLUICtrl(p), + mMaxLengthBytes(p.max_length.bytes), + mMaxLengthChars(p.max_length.chars), + mCursorPos( 0 ), + mScrollHPos( 0 ), + mTextPadLeft(p.text_pad_left), + mTextPadRight(p.text_pad_right), + mTextLeftEdge(0), // computed in updateTextPadding() below + mTextRightEdge(0), // computed in updateTextPadding() below + mCommitOnFocusLost( p.commit_on_focus_lost ), + mKeystrokeOnEsc(false), + mRevertOnEsc( p.revert_on_esc ), + mKeystrokeCallback( p.keystroke_callback() ), + mIsSelecting( false ), + mSelectionStart( 0 ), + mSelectionEnd( 0 ), + mLastSelectionX(-1), + mLastSelectionY(-1), + mLastSelectionStart(-1), + mLastSelectionEnd(-1), + mBorderThickness( 0 ), + mIgnoreArrowKeys( false ), + mIgnoreTab( p.ignore_tab ), + mDrawAsterixes( p.is_password ), + mAllowEmoji( p.allow_emoji ), + mSpellCheck( p.spellcheck ), + mSpellCheckStart(-1), + mSpellCheckEnd(-1), + mSelectAllonFocusReceived( p.select_on_focus ), + mSelectAllonCommit( true ), + mPassDelete(false), + mReadOnly(false), + mBgImage( p.background_image ), + mBgImageDisabled( p.background_image_disabled ), + mBgImageFocused( p.background_image_focused ), + mShowImageFocused( p.bg_image_always_focused ), + mShowLabelFocused( p.show_label_focused ), + mUseBgColor(p.use_bg_color), + mHaveHistory(false), + mReplaceNewlinesWithSpaces( true ), + mPrevalidator(p.prevalidator()), + mInputPrevalidator(p.input_prevalidator()), + mLabel(p.label), + mCursorColor(p.cursor_color()), + mBgColor(p.bg_color()), + mFgColor(p.text_color()), + mReadOnlyFgColor(p.text_readonly_color()), + mTentativeFgColor(p.text_tentative_color()), + mHighlightColor(p.highlight_color()), + mPreeditBgColor(p.preedit_bg_color()), + mGLFont(p.font), + mContextMenuHandle(), + mShowContextMenu(true) +{ + llassert( mMaxLengthBytes > 0 ); + + LLUICtrl::setEnabled(true); + setEnabled(p.enabled); + + mScrollTimer.reset(); + mTripleClickTimer.reset(); + setText(p.default_text()); + + if (p.initial_value.isProvided() + && !p.control_name.isProvided()) + { + // Initial value often is descriptive, like "Type some ID here" + // and can be longer than size limitation, ignore size + setText(p.initial_value.getValue().asString(), false); + } + + // Initialize current history line iterator + mCurrentHistoryLine = mLineHistory.begin(); + + LLRect border_rect(getLocalRect()); + // adjust for gl line drawing glitch + border_rect.mTop -= 1; + border_rect.mRight -=1; + LLViewBorder::Params border_p(p.border); + border_p.rect = border_rect; + border_p.follows.flags = FOLLOWS_ALL; + border_p.bevel_style = LLViewBorder::BEVEL_IN; + mBorder = LLUICtrlFactory::create(border_p); + addChild( mBorder ); + + // clamp text padding to current editor size + updateTextPadding(); + setCursor(mText.length()); + + if (mSpellCheck) + { + LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this)); + } + mSpellCheckTimer.reset(); + + updateAllowingLanguageInput(); +} + +LLLineEditor::~LLLineEditor() +{ + mCommitOnFocusLost = false; + + // Make sure no context menu linger around once the widget is deleted + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (menu) + { + menu->hide(); + } + setContextMenu(NULL); + + // calls onCommit() while LLLineEditor still valid + gFocusMgr.releaseFocusIfNeeded( this ); +} + +void LLLineEditor::initFromParams(const LLLineEditor::Params& params) +{ + LLUICtrl::initFromParams(params); + LLUICtrl::setEnabled(true); + setEnabled(params.enabled); +} + +void LLLineEditor::onFocusReceived() +{ + gEditMenuHandler = this; + LLUICtrl::onFocusReceived(); + updateAllowingLanguageInput(); +} + +void LLLineEditor::onFocusLost() +{ + // The call to updateAllowLanguageInput() + // when loosing the keyboard focus *may* + // indirectly invoke handleUnicodeCharHere(), + // so it must be called before onCommit. + updateAllowingLanguageInput(); + + if( mCommitOnFocusLost && mText.getString() != mPrevText) + { + onCommit(); + } + + if( gEditMenuHandler == this ) + { + gEditMenuHandler = NULL; + } + + getWindow()->showCursorFromMouseMove(); + + LLUICtrl::onFocusLost(); +} + +// virtual +void LLLineEditor::onCommit() +{ + // put current line into the line history + updateHistory(); + + setControlValue(getValue()); + LLUICtrl::onCommit(); + resetDirty(); + + // Selection on commit needs to be turned off when evaluating maths + // expressions, to allow indication of the error position + if (mSelectAllonCommit) selectAll(); +} + +// Returns true if user changed value at all +// virtual +bool LLLineEditor::isDirty() const +{ + return mText.getString() != mPrevText; +} + +// Clear dirty state +// virtual +void LLLineEditor::resetDirty() +{ + mPrevText = mText.getString(); +} + +// assumes UTF8 text +// virtual +void LLLineEditor::setValue(const LLSD& value ) +{ + setText(value.asString()); +} + +//virtual +LLSD LLLineEditor::getValue() const +{ + return LLSD(getText()); +} + + +// line history support +void LLLineEditor::updateHistory() +{ + // On history enabled line editors, remember committed line and + // reset current history line number. + // Be sure only to remember lines that are not empty and that are + // different from the last on the list. + if( mHaveHistory && getLength() ) + { + if( !mLineHistory.empty() ) + { + // When not empty, last line of history should always be blank. + if( mLineHistory.back().empty() ) + { + // discard the empty line + mLineHistory.pop_back(); + } + else + { + LL_WARNS("") << "Last line of history was not blank." << LL_ENDL; + } + } + + // Add text to history, ignoring duplicates + if( mLineHistory.empty() || getText() != mLineHistory.back() ) + { + mLineHistory.push_back( getText() ); + } + + // Restore the blank line and set mCurrentHistoryLine to point at it + mLineHistory.push_back( "" ); + mCurrentHistoryLine = mLineHistory.end() - 1; + } +} + +void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + updateTextPadding(); // For clamping side-effect. + setCursor(mCursorPos); // For clamping side-effect. +} + +void LLLineEditor::setEnabled(bool enabled) +{ + mReadOnly = !enabled; + setTabStop(!mReadOnly); + updateAllowingLanguageInput(); +} + + +void LLLineEditor::setMaxTextLength(S32 max_text_length) +{ + S32 max_len = llmax(0, max_text_length); + mMaxLengthBytes = max_len; +} + +void LLLineEditor::setMaxTextChars(S32 max_text_chars) +{ + S32 max_chars = llmax(0, max_text_chars); + mMaxLengthChars = max_chars; +} + +void LLLineEditor::getTextPadding(S32 *left, S32 *right) +{ + *left = mTextPadLeft; + *right = mTextPadRight; +} + +void LLLineEditor::setTextPadding(S32 left, S32 right) +{ + mTextPadLeft = left; + mTextPadRight = right; + updateTextPadding(); +} + +void LLLineEditor::updateTextPadding() +{ + mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth()); + mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth()); +} + + +void LLLineEditor::setText(const LLStringExplicit &new_text) +{ + setText(new_text, true); +} + +void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit) +{ + // If new text is identical, don't copy and don't move insertion point + if (mText.getString() == new_text) + { + return; + } + + // Check to see if entire field is selected. + S32 len = mText.length(); + bool all_selected = (len > 0) + && (( mSelectionStart == 0 && mSelectionEnd == len ) + || ( mSelectionStart == len && mSelectionEnd == 0 )); + + // Do safe truncation so we don't split multi-byte characters + // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor + all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived); + + std::string truncated_utf8 = new_text; + if (!mAllowEmoji) + { + // Cut emoji symbols if exist + utf8str_remove_emojis(truncated_utf8); + } + if (use_size_limit && truncated_utf8.size() > (U32)mMaxLengthBytes) + { + truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes); + } + mText.assign(truncated_utf8); + + if (use_size_limit && mMaxLengthChars) + { + mText.assign(utf8str_symbol_truncate(truncated_utf8, mMaxLengthChars)); + } + + if (all_selected) + { + // ...keep whole thing selected + selectAll(); + } + else + { + // try to preserve insertion point, but deselect text + deselect(); + } + setCursor(llmin((S32)mText.length(), getCursor())); + + // Set current history line to end of history. + if (mLineHistory.empty()) + { + mCurrentHistoryLine = mLineHistory.end(); + } + else + { + mCurrentHistoryLine = mLineHistory.end() - 1; + } + + mPrevText = mText; +} + + +// Picks a new cursor position based on the actual screen size of text being drawn. +void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x ) +{ + S32 cursor_pos = calcCursorPos(local_mouse_x); + + S32 left_pos = llmin( mSelectionStart, cursor_pos ); + S32 length = llabs( mSelectionStart - cursor_pos ); + const LLWString& substr = mText.getWString().substr(left_pos, length); + + if (mIsSelecting && !prevalidateInput(substr)) + return; + + setCursor(cursor_pos); +} + +void LLLineEditor::setCursor( S32 pos ) +{ + S32 old_cursor_pos = getCursor(); + mCursorPos = llclamp( pos, 0, mText.length()); + + // position of end of next character after cursor + S32 pixels_after_scroll = findPixelNearestPos(); + if( pixels_after_scroll > mTextRightEdge ) + { + S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos); + S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left))); + // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters) + // or first character if cursor is at beginning + S32 new_last_visible_char = llmax(0, getCursor() - 1); + S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char); + if (old_cursor_pos == last_visible_char) + { + mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD)); + } + else + { + mScrollHPos = min_scroll; + } + } + else if (getCursor() < mScrollHPos) + { + if (old_cursor_pos == mScrollHPos) + { + mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL)); + } + else + { + mScrollHPos = getCursor(); + } + } +} + + +void LLLineEditor::setCursorToEnd() +{ + setCursor(mText.length()); + deselect(); +} + +void LLLineEditor::resetScrollPosition() +{ + mScrollHPos = 0; + // make sure cursor says in visible range + setCursor(getCursor()); +} + +bool LLLineEditor::canDeselect() const +{ + return hasSelection(); +} + +void LLLineEditor::deselect() +{ + mSelectionStart = 0; + mSelectionEnd = 0; + mIsSelecting = false; +} + + +void LLLineEditor::startSelection() +{ + mIsSelecting = true; + mSelectionStart = getCursor(); + mSelectionEnd = getCursor(); +} + +void LLLineEditor::endSelection() +{ + if( mIsSelecting ) + { + mIsSelecting = false; + mSelectionEnd = getCursor(); + } +} + +bool LLLineEditor::canSelectAll() const +{ + return true; +} + +void LLLineEditor::selectAll() +{ + if (!prevalidateInput(mText.getWString())) + { + return; + } + + mSelectionStart = mText.length(); + mSelectionEnd = 0; + setCursor(mSelectionEnd); + //mScrollHPos = 0; + mIsSelecting = true; + updatePrimary(); +} + +bool LLLineEditor::getSpellCheck() const +{ + return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck); +} + +const std::string& LLLineEditor::getSuggestion(U32 index) const +{ + return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null; +} + +U32 LLLineEditor::getSuggestionCount() const +{ + return mSuggestionList.size(); +} + +void LLLineEditor::replaceWithSuggestion(U32 index) +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) + { + LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]); + if (!mAllowEmoji) + { + // Cut emoji symbols if exist + wstring_remove_emojis(suggestion); + } + if (suggestion.empty()) + return; + + deselect(); + + // Delete the misspelled word + mText.erase(it->first, it->second - it->first); + + // Insert the suggestion in its place + mText.insert(it->first, suggestion); + setCursor(it->first + (S32)suggestion.length()); + + break; + } + } + mSpellCheckStart = mSpellCheckEnd = -1; +} + +void LLLineEditor::addToDictionary() +{ + if (canAddToDictionary()) + { + LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos)); + } +} + +bool LLLineEditor::canAddToDictionary() const +{ + return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); +} + +void LLLineEditor::addToIgnore() +{ + if (canAddToIgnore()) + { + LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos)); + } +} + +bool LLLineEditor::canAddToIgnore() const +{ + return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); +} + +std::string LLLineEditor::getMisspelledWord(U32 pos) const +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= pos) && (it->second >= pos) ) + { + return wstring_to_utf8str(mText.getWString().substr(it->first, it->second - it->first)); + } + } + return LLStringUtil::null; +} + +bool LLLineEditor::isMisspelledWord(U32 pos) const +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= pos) && (it->second >= pos) ) + { + return true; + } + } + return false; +} + +void LLLineEditor::onSpellCheckSettingsChange() +{ + // Recheck the spelling on every change + mMisspellRanges.clear(); + mSpellCheckStart = mSpellCheckEnd = -1; +} + +bool LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + setFocus( true ); + mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); + + if (mSelectionEnd == 0 && mSelectionStart == mText.length()) + { + // if everything is selected, handle this as a normal click to change insertion point + handleMouseDown(x, y, mask); + } + else + { + const LLWString& wtext = mText.getWString(); + + bool doSelectAll = true; + + // Select the word we're on + if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) + { + S32 old_selection_start = mLastSelectionStart; + S32 old_selection_end = mLastSelectionEnd; + + // Select word the cursor is over + while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] )) + { // Find the start of the word + mCursorPos--; + } + startSelection(); + + while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) + { // Find the end of the word + mCursorPos++; + } + mSelectionEnd = mCursorPos; + + // If nothing changed, then the word was already selected. Select the whole line. + doSelectAll = (old_selection_start == mSelectionStart) && + (old_selection_end == mSelectionEnd); + } + + if ( doSelectAll ) + { // Select everything + selectAll(); + } + } + + // We don't want handleMouseUp() to "finish" the selection (and thereby + // set mSelectionEnd to where the mouse is), so we finish the selection + // here. + mIsSelecting = false; + + // delay cursor flashing + mKeystrokeTimer.reset(); + + // take selection to 'primary' clipboard + updatePrimary(); + + return true; +} + +bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // Check first whether the "clear search" button wants to deal with this. + if(childrenHandleMouseDown(x, y, mask) != NULL) + { + return true; + } + + if (!mSelectAllonFocusReceived + || gFocusMgr.getKeyboardFocus() == this) + { + mLastSelectionStart = -1; + mLastSelectionStart = -1; + + if (mask & MASK_SHIFT) + { + // assume we're starting a drag select + mIsSelecting = true; + + // Handle selection extension + S32 old_cursor_pos = getCursor(); + setCursorAtLocalPos(x); + + if (hasSelection()) + { + /* Mac-like behavior - extend selection towards the cursor + if (getCursor() < mSelectionStart + && getCursor() < mSelectionEnd) + { + // ...left of selection + mSelectionStart = llmax(mSelectionStart, mSelectionEnd); + mSelectionEnd = getCursor(); + } + else if (getCursor() > mSelectionStart + && getCursor() > mSelectionEnd) + { + // ...right of selection + mSelectionStart = llmin(mSelectionStart, mSelectionEnd); + mSelectionEnd = getCursor(); + } + else + { + mSelectionEnd = getCursor(); + } + */ + // Windows behavior + mSelectionEnd = getCursor(); + } + else + { + mSelectionStart = old_cursor_pos; + mSelectionEnd = getCursor(); + } + } + else + { + if (mTripleClickTimer.hasExpired()) + { + // Save selection for word/line selecting on double-click + mLastSelectionStart = mSelectionStart; + mLastSelectionEnd = mSelectionEnd; + + // Move cursor and deselect for regular click + setCursorAtLocalPos( x ); + deselect(); + startSelection(); + } + else // handle triple click + { + selectAll(); + // We don't want handleMouseUp() to "finish" the selection (and thereby + // set mSelectionEnd to where the mouse is), so we finish the selection + // here. + mIsSelecting = false; + } + } + + gFocusMgr.setMouseCapture( this ); + } + + setFocus(true); + + // delay cursor flashing + mKeystrokeTimer.reset(); + + if (mMouseDownSignal) + (*mMouseDownSignal)(this,x,y,mask); + + return true; +} + +bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + // LL_INFOS() << "MiddleMouseDown" << LL_ENDL; + setFocus( true ); + if( canPastePrimary() ) + { + setCursorAtLocalPos(x); + pastePrimary(); + } + return true; +} + +bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + setFocus(true); + if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu()) + { + showContextMenu(x, y); + } + return true; +} + +bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = false; + // Check first whether the "clear search" button wants to deal with this. + if(!hasMouseCapture()) + { + if(childrenHandleHover(x, y, mask) != NULL) + { + return true; + } + } + + if( (hasMouseCapture()) && mIsSelecting ) + { + if (x != mLastSelectionX || y != mLastSelectionY) + { + mLastSelectionX = x; + mLastSelectionY = y; + } + // Scroll if mouse cursor outside of bounds + if (mScrollTimer.hasExpired()) + { + S32 increment = ll_round(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME); + mScrollTimer.reset(); + mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME); + if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) ) + { + // Scroll to the left + mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length()); + } + else + if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) ) + { + // If scrolling one pixel would make a difference... + S32 pixels_after_scrolling_one_char = findPixelNearestPos(1); + if( pixels_after_scrolling_one_char >= mTextRightEdge ) + { + // ...scroll to the right + mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length()); + } + } + } + + setCursorAtLocalPos( x ); + mSelectionEnd = getCursor(); + + // delay cursor flashing + mKeystrokeTimer.reset(); + + getWindow()->setCursor(UI_CURSOR_IBEAM); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; + handled = true; + } + + if( !handled ) + { + getWindow()->setCursor(UI_CURSOR_IBEAM); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; + handled = true; + } + + return handled; +} + + +bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if( hasMouseCapture() ) + { + gFocusMgr.setMouseCapture( NULL ); + handled = true; + } + + // Check first whether the "clear search" button wants to deal with this. + if(!handled && childrenHandleMouseUp(x, y, mask) != NULL) + { + return true; + } + + if( mIsSelecting ) + { + setCursorAtLocalPos( x ); + mSelectionEnd = getCursor(); + + handled = true; + } + + if( handled ) + { + // delay cursor flashing + mKeystrokeTimer.reset(); + + // take selection to 'primary' clipboard + updatePrimary(); + } + + // We won't call LLUICtrl::handleMouseUp to avoid double calls of childrenHandleMouseUp().Just invoke the signal manually. + if (mMouseUpSignal) + (*mMouseUpSignal)(this,x,y, mask); + return handled; +} + + +// Remove a single character from the text +void LLLineEditor::removeChar() +{ + if( getCursor() > 0 ) + { + if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1))) + return; + + mText.erase(getCursor() - 1, 1); + + setCursor(getCursor() - 1); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } +} + +void LLLineEditor::addChar(const llwchar uni_char) +{ + if (!mAllowEmoji && LLStringOps::isEmoji(uni_char)) + return; + + llwchar new_c = uni_char; + if (hasSelection()) + { + deleteSelection(); + } + else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + if (!prevalidateInput(mText.getWString().substr(getCursor(), 1))) + return; + + mText.erase(getCursor(), 1); + } + + S32 cur_bytes = mText.getString().size(); + + S32 new_bytes = wchar_utf8_length(new_c); + + bool allow_char = true; + + // Check byte length limit + if ((new_bytes + cur_bytes) > mMaxLengthBytes) + { + allow_char = false; + } + else if (mMaxLengthChars) + { + S32 wide_chars = mText.getWString().size(); + if ((wide_chars + 1) > mMaxLengthChars) + { + allow_char = false; + } + } + + if (allow_char) + { + // Will we need to scroll? + LLWString w_buf; + w_buf.assign(1, new_c); + + mText.insert(getCursor(), w_buf); + setCursor(getCursor() + 1); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + + getWindow()->hideCursorUntilMouseMove(); +} + +// Extends the selection box to the new cursor position +void LLLineEditor::extendSelection( S32 new_cursor_pos ) +{ + if( !mIsSelecting ) + { + startSelection(); + } + + S32 left_pos = llmin( mSelectionStart, new_cursor_pos ); + S32 selection_length = llabs( mSelectionStart - new_cursor_pos ); + const LLWString& selection = mText.getWString().substr(left_pos, selection_length); + + if (!prevalidateInput(selection)) + return; + + setCursor(new_cursor_pos); + mSelectionEnd = getCursor(); +} + + +void LLLineEditor::setSelection(S32 start, S32 end) +{ + S32 len = mText.length(); + + mIsSelecting = true; + + // JC, yes, this seems odd, but I think you have to presume a + // selection dragged from the end towards the start. + mSelectionStart = llclamp(end, 0, len); + mSelectionEnd = llclamp(start, 0, len); + setCursor(start); +} + +void LLLineEditor::setDrawAsterixes(bool b) +{ + mDrawAsterixes = b; + updateAllowingLanguageInput(); +} + +S32 LLLineEditor::prevWordPos(S32 cursorPos) const +{ + const LLWString& wtext = mText.getWString(); + while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') ) + { + cursorPos--; + } + while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) + { + cursorPos--; + } + return cursorPos; +} + +S32 LLLineEditor::nextWordPos(S32 cursorPos) const +{ + const LLWString& wtext = mText.getWString(); + while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) + { + cursorPos++; + } + while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') ) + { + cursorPos++; + } + return cursorPos; +} + + +bool LLLineEditor::handleSelectionKey(KEY key, MASK mask) +{ + bool handled = false; + + if( mask & MASK_SHIFT ) + { + handled = true; + + switch( key ) + { + case KEY_LEFT: + if( 0 < getCursor() ) + { + S32 cursorPos = getCursor() - 1; + if( mask & MASK_CONTROL ) + { + cursorPos = prevWordPos(cursorPos); + } + extendSelection( cursorPos ); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + break; + + case KEY_RIGHT: + if( getCursor() < mText.length()) + { + S32 cursorPos = getCursor() + 1; + if( mask & MASK_CONTROL ) + { + cursorPos = nextWordPos(cursorPos); + } + extendSelection( cursorPos ); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + break; + + case KEY_PAGE_UP: + case KEY_HOME: + extendSelection( 0 ); + break; + + case KEY_PAGE_DOWN: + case KEY_END: + { + S32 len = mText.length(); + if( len ) + { + extendSelection( len ); + } + break; + } + + default: + handled = false; + break; + } + } + + if(handled) + { + // take selection to 'primary' clipboard + updatePrimary(); + } + + return handled; +} + +void LLLineEditor::deleteSelection() +{ + if( !mReadOnly && hasSelection() ) + { + S32 left_pos, selection_length; + getSelectionRange(&left_pos, &selection_length); + const LLWString& selection = mText.getWString().substr(left_pos, selection_length); + + if (!prevalidateInput(selection)) + return; + + mText.erase(left_pos, selection_length); + deselect(); + setCursor(left_pos); + } +} + +bool LLLineEditor::canCut() const +{ + return !mReadOnly && !mDrawAsterixes && hasSelection(); +} + +// cut selection to clipboard +void LLLineEditor::cut() +{ + if( canCut() ) + { + S32 left_pos, length; + getSelectionRange(&left_pos, &length); + const LLWString& selection = mText.getWString().substr(left_pos, length); + + if (!prevalidateInput(selection)) + return; + + // Prepare for possible rollback + LLLineEditorRollback rollback( this ); + + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); + deleteSelection(); + + // Validate new string and rollback the if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback( this ); + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + else + { + onKeystroke(); + } + } +} + +bool LLLineEditor::canCopy() const +{ + return !mDrawAsterixes && hasSelection(); +} + + +// copy selection to clipboard +void LLLineEditor::copy() +{ + if( canCopy() ) + { + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); + } +} + +bool LLLineEditor::canPaste() const +{ + return !mReadOnly && LLClipboard::instance().isTextAvailable(); +} + +void LLLineEditor::paste() +{ + bool is_primary = false; + pasteHelper(is_primary); +} + +void LLLineEditor::pastePrimary() +{ + bool is_primary = true; + pasteHelper(is_primary); +} + +// paste from primary (is_primary==true) or clipboard (is_primary==false) +void LLLineEditor::pasteHelper(bool is_primary) +{ + bool can_paste_it; + if (is_primary) + { + can_paste_it = canPastePrimary(); + } + else + { + can_paste_it = canPaste(); + } + + if (can_paste_it) + { + LLWString paste; + LLClipboard::instance().pasteFromClipboard(paste, is_primary); + + if (!paste.empty()) + { + if (!mAllowEmoji) + { + wstring_remove_emojis(paste); + } + + if (!prevalidateInput(paste)) + return; + + // Prepare for possible rollback + LLLineEditorRollback rollback(this); + + // Delete any selected characters + if ((!is_primary) && hasSelection()) + { + deleteSelection(); + } + + // Clean up string (replace tabs and returns and remove characters that our fonts don't support.) + LLWString clean_string(paste); + LLWStringUtil::replaceTabsWithSpaces(clean_string, 1); + //clean_string = wstring_detabify(paste, 1); + LLWStringUtil::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182); // 182 == paragraph character + + // Insert the string + + // Check to see that the size isn't going to be larger than the max number of bytes + U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText); + + if ( available_bytes < (U32) wstring_utf8_length(clean_string) ) + { // Doesn't all fit + llwchar current_symbol = clean_string[0]; + U32 wchars_that_fit = 0; + U32 total_bytes = wchar_utf8_length(current_symbol); + + //loop over the "wide" characters (symbols) + //and check to see how large (in bytes) each symbol is. + while ( total_bytes <= available_bytes ) + { + //while we still have available bytes + //"accept" the current symbol and check the size + //of the next one + current_symbol = clean_string[++wchars_that_fit]; + total_bytes += wchar_utf8_length(current_symbol); + } + // Truncate the clean string at the limit of what will fit + clean_string = clean_string.substr(0, wchars_that_fit); + LLUI::getInstance()->reportBadKeystroke(); + } + + if (mMaxLengthChars) + { + U32 available_chars = mMaxLengthChars - mText.getWString().size(); + + if (available_chars < clean_string.size()) + { + clean_string = clean_string.substr(0, available_chars); + } + + LLUI::getInstance()->reportBadKeystroke(); + } + + mText.insert(getCursor(), clean_string); + setCursor( getCursor() + (S32)clean_string.length() ); + deselect(); + + // Validate new string and rollback the if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback( this ); + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + else + { + onKeystroke(); + } + } + } +} + +// copy selection to primary +void LLLineEditor::copyPrimary() +{ + if( canCopy() ) + { + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true); + } +} + +bool LLLineEditor::canPastePrimary() const +{ + return !mReadOnly && LLClipboard::instance().isTextAvailable(true); +} + +void LLLineEditor::updatePrimary() +{ + if(canCopy() ) + { + copyPrimary(); + } +} + +bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) +{ + bool handled = false; + + switch( key ) + { + case KEY_INSERT: + if (mask == MASK_NONE) + { + gKeyboard->toggleInsertMode(); + } + + handled = true; + break; + + case KEY_BACKSPACE: + if (!mReadOnly) + { + //LL_INFOS() << "Handling backspace" << LL_ENDL; + if( hasSelection() ) + { + deleteSelection(); + } + else + if( 0 < getCursor() ) + { + removeChar(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + } + handled = true; + break; + + case KEY_PAGE_UP: + case KEY_HOME: + if (!mIgnoreArrowKeys) + { + setCursor(0); + handled = true; + } + break; + + case KEY_PAGE_DOWN: + case KEY_END: + if (!mIgnoreArrowKeys) + { + S32 len = mText.length(); + if( len ) + { + setCursor(len); + } + handled = true; + } + break; + + case KEY_LEFT: + if (mIgnoreArrowKeys && mask == MASK_NONE) + break; + if ((mask & MASK_ALT) == 0) + { + if( hasSelection() ) + { + setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd )); + } + else + if( 0 < getCursor() ) + { + S32 cursorPos = getCursor() - 1; + if( mask & MASK_CONTROL ) + { + cursorPos = prevWordPos(cursorPos); + } + setCursor(cursorPos); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + case KEY_RIGHT: + if (mIgnoreArrowKeys && mask == MASK_NONE) + break; + if ((mask & MASK_ALT) == 0) + { + if (hasSelection()) + { + setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd)); + } + else + if (getCursor() < mText.length()) + { + S32 cursorPos = getCursor() + 1; + if( mask & MASK_CONTROL ) + { + cursorPos = nextWordPos(cursorPos); + } + setCursor(cursorPos); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + // handle ctrl-uparrow if we have a history enabled line editor. + case KEY_UP: + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) + { + if (mCurrentHistoryLine > mLineHistory.begin()) + { + mText.assign(*(--mCurrentHistoryLine)); + setCursorToEnd(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + // handle [ctrl]-downarrow if we have a history enabled line editor + case KEY_DOWN: + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) + { + if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1) + { + mText.assign( *(++mCurrentHistoryLine) ); + setCursorToEnd(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + case KEY_RETURN: + // store sent line in history + updateHistory(); + break; + + case KEY_ESCAPE: + if (mRevertOnEsc && mText.getString() != mPrevText) + { + setText(mPrevText); + // Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged) + if (mKeystrokeOnEsc) + { + onKeystroke(); + } + } + break; + + default: + break; + } + + return handled; +} + + +bool LLLineEditor::handleKeyHere(KEY key, MASK mask ) +{ + bool handled = false; + bool selection_modified = false; + + if ( gFocusMgr.getKeyboardFocus() == this ) + { + LLLineEditorRollback rollback( this ); + + if( !handled ) + { + handled = handleSelectionKey( key, mask ); + selection_modified = handled; + } + + // Handle most keys only if the text editor is writeable. + if ( !mReadOnly ) + { + if( !handled ) + { + handled = handleSpecialKey( key, mask ); + } + } + + if( handled ) + { + mKeystrokeTimer.reset(); + + // Most keystrokes will make the selection box go away, but not all will. + if( !selection_modified && + KEY_SHIFT != key && + KEY_CONTROL != key && + KEY_ALT != key && + KEY_CAPSLOCK != key) + { + deselect(); + } + + bool prevalidator_failed = false; + + // If read-only, don't allow changes + bool need_to_rollback = mReadOnly && (mText.getString() == rollback.getText()); + + // Validate new string and rollback the keystroke if needed. + if (!need_to_rollback && mPrevalidator) + { + prevalidator_failed = !mPrevalidator.validate(mText.getWString()); + need_to_rollback |= prevalidator_failed; + } + + if (need_to_rollback) + { + rollback.doRollback(this); + + LLUI::getInstance()->reportBadKeystroke(); + if (prevalidator_failed) + { + mPrevalidator.showLastErrorUsingTimeout(); + } + } + + // Notify owner if requested + if (!need_to_rollback && handled) + { + onKeystroke(); + if ( (!selection_modified) && (KEY_BACKSPACE == key) ) + { + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); + } + } + } + } + + return handled; +} + + +bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) +{ + if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL + { + return false; + } + + bool handled = false; + + if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly) + { + handled = true; + + LLLineEditorRollback rollback( this ); + + { + LLWString u_char; + u_char.assign(1, uni_char); + if (!prevalidateInput(u_char)) + return handled; + } + + addChar(uni_char); + + mKeystrokeTimer.reset(); + + deselect(); + + // Validate new string and rollback the keystroke if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback( this ); + + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + + // Notify owner if requested + if (!need_to_rollback && handled) + { + // HACK! The only usage of this callback doesn't do anything with the character. + // We'll have to do something about this if something ever changes! - Doug + onKeystroke(); + + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); + } + } + return handled; +} + + +bool LLLineEditor::canDoDelete() const +{ + return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) ); +} + +void LLLineEditor::doDelete() +{ + if (canDoDelete() && mText.length() > 0) + { + // Prepare for possible rollback + LLLineEditorRollback rollback( this ); + + if (hasSelection()) + { + deleteSelection(); + } + else if ( getCursor() < mText.length()) + { + const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1); + + if (!prevalidateInput(text_to_delete)) + { + onKeystroke(); + return; + } + setCursor(getCursor() + 1); + removeChar(); + } + + // Validate new string and rollback the if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback(this); + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + else + { + onKeystroke(); + + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); + } + } +} + + +void LLLineEditor::drawBackground() +{ + F32 alpha = getCurrentTransparency(); + if (mUseBgColor) + { + gl_rect_2d(getLocalRect(), mBgColor % alpha, true); + } + else + { + bool has_focus = hasFocus(); + LLUIImage* image; + if (mReadOnly) + { + image = mBgImageDisabled; + } + else if (has_focus || mShowImageFocused) + { + image = mBgImageFocused; + } + else + { + image = mBgImage; + } + + if (!image) return; + // optionally draw programmatic border + if (has_focus) + { + LLColor4 tmp_color = gFocusMgr.getFocusColor(); + tmp_color.setAlpha(alpha); + image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(), + tmp_color, + gFocusMgr.getFocusFlashWidth()); + } + LLColor4 tmp_color = UI_VERTEX_COLOR; + tmp_color.setAlpha(alpha); + image->draw(getLocalRect(), tmp_color); + } +} + +//virtual +void LLLineEditor::draw() +{ + F32 alpha = getDrawContext().mAlpha; + S32 text_len = mText.length(); + static LLUICachedControl lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0); + static LLUICachedControl preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); + static LLUICachedControl preedit_marker_gap ("UIPreeditMarkerGap", 0); + static LLUICachedControl preedit_marker_position ("UIPreeditMarkerPosition", 0); + static LLUICachedControl preedit_marker_thickness ("UIPreeditMarkerThickness", 0); + static LLUICachedControl preedit_standout_brightness ("UIPreeditStandoutBrightness", 0); + static LLUICachedControl preedit_standout_gap ("UIPreeditStandoutGap", 0); + static LLUICachedControl preedit_standout_position ("UIPreeditStandoutPosition", 0); + static LLUICachedControl preedit_standout_thickness ("UIPreeditStandoutThickness", 0); + + std::string saved_text; + if (mDrawAsterixes) + { + saved_text = mText.getString(); + std::string text; + for (S32 i = 0; i < mText.length(); i++) + { + text += PASSWORD_ASTERISK; + } + mText = text; + } + + // draw rectangle for the background + LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 ); + background.stretch( -mBorderThickness ); + + S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2; + if (mSpellCheck) + { + lineeditor_v_pad += 1; + } + + drawBackground(); + + // draw text + + // With viewer-2 art files, input region is 2 pixels up + S32 cursor_bottom = background.mBottom + 2; + S32 cursor_top = background.mTop - 1; + + LLColor4 text_color; + if (!mReadOnly) + { + if (!getTentative()) + { + text_color = mFgColor.get(); + } + else + { + text_color = mTentativeFgColor.get(); + } + } + else + { + text_color = mReadOnlyFgColor.get(); + } + text_color.setAlpha(alpha); + LLColor4 label_color = mTentativeFgColor.get(); + label_color.setAlpha(alpha); + + if (hasPreeditString()) + { + // Draw preedit markers. This needs to be before drawing letters. + for (U32 i = 0; i < mPreeditStandouts.size(); i++) + { + const S32 preedit_left = mPreeditPositions[i]; + const S32 preedit_right = mPreeditPositions[i + 1]; + if (preedit_right > mScrollHPos) + { + S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor()); + S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight); + if (preedit_pixels_left >= background.mRight) + { + break; + } + if (mPreeditStandouts[i]) + { + gl_rect_2d(preedit_pixels_left + preedit_standout_gap, + background.mBottom + preedit_standout_position, + preedit_pixels_right - preedit_standout_gap - 1, + background.mBottom + preedit_standout_position - preedit_standout_thickness, + (text_color * preedit_standout_brightness + + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/)); + } + else + { + gl_rect_2d(preedit_pixels_left + preedit_marker_gap, + background.mBottom + preedit_marker_position, + preedit_pixels_right - preedit_marker_gap - 1, + background.mBottom + preedit_marker_position - preedit_marker_thickness, + (text_color * preedit_marker_brightness + + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/)); + } + } + } + } + + S32 rendered_text = 0; + F32 rendered_pixels_right = (F32)mTextLeftEdge; + F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad; + + if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() ) + { + S32 select_left; + S32 select_right; + if (mSelectionStart < mSelectionEnd) + { + select_left = mSelectionStart; + select_right = mSelectionEnd; + } + else + { + select_left = mSelectionEnd; + select_right = mSelectionStart; + } + + if( select_left > mScrollHPos ) + { + // unselected, left side + rendered_text = mGLFont->render( + mText, mScrollHPos, + rendered_pixels_right, text_bottom, + text_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + select_left - mScrollHPos, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + + if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) + { + LLColor4 color = mHighlightColor; + color.setAlpha(alpha); + // selected middle + S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); + width = llmin(width, mTextRightEdge - ll_round(rendered_pixels_right)); + gl_rect_2d(ll_round(rendered_pixels_right), cursor_top, ll_round(rendered_pixels_right)+width, cursor_bottom, color); + + LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); + rendered_text += mGLFont->render( + mText, mScrollHPos + rendered_text, + rendered_pixels_right, text_bottom, + tmp_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + select_right - mScrollHPos - rendered_text, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + + if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) + { + // unselected, right side + rendered_text += mGLFont->render( + mText, mScrollHPos + rendered_text, + rendered_pixels_right, text_bottom, + text_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + } + else + { + rendered_text = mGLFont->render( + mText, mScrollHPos, + rendered_pixels_right, text_bottom, + text_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } +#if 1 // for when we're ready for image art. + mBorder->setVisible(false); // no more programmatic art. +#endif + + if ( (getSpellCheck()) && (mText.length() > 2) ) + { + // Calculate start and end indices for the first and last visible word + U32 start = prevWordPos(mScrollHPos), end = nextWordPos(mScrollHPos + rendered_text); + + if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) ) + { + const LLWString& text = mText.getWString().substr(start, end); + + // Find the start of the first word + U32 word_start = 0, word_end = 0; + while ( (word_start < text.length()) && (!LLStringOps::isAlpha(text[word_start])) ) + { + word_start++; + } + + // Iterate over all words in the text block and check them one by one + mMisspellRanges.clear(); + while (word_start < text.length()) + { + // Find the end of the current word (special case handling for "'" when it's used as a contraction) + word_end = word_start + 1; + while ( (word_end < text.length()) && + ((LLWStringUtil::isPartOfWord(text[word_end])) || + ((L'\'' == text[word_end]) && (word_end + 1 < text.length()) && + (LLStringOps::isAlnum(text[word_end - 1])) && (LLStringOps::isAlnum(text[word_end + 1])))) ) + { + word_end++; + } + if (word_end > text.length()) + { + break; + } + + // Don't process words shorter than 3 characters + std::string word = wstring_to_utf8str(text.substr(word_start, word_end - word_start)); + if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) + { + mMisspellRanges.push_back(std::pair(start + word_start, start + word_end)); + } + + // Find the start of the next word + word_start = word_end + 1; + while ( (word_start < text.length()) && (!LLWStringUtil::isPartOfWord(text[word_start])) ) + { + word_start++; + } + } + + mSpellCheckStart = start; + mSpellCheckEnd = end; + } + + // Draw squiggly lines under any (visible) misspelled words + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + // Skip over words that aren't (partially) visible + if ( ((it->first < start) && (it->second < start)) || (it->first > end) ) + { + continue; + } + + // Skip the current word if the user is still busy editing it + if ( (!mSpellCheckTimer.hasExpired()) && (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) + { + continue; + } + + S32 pxWidth = getRect().getWidth(); + S32 pxStart = findPixelNearestPos(it->first - getCursor()); + if (pxStart > pxWidth) + { + continue; + } + S32 pxEnd = findPixelNearestPos(it->second - getCursor()); + if (pxEnd > pxWidth) + { + pxEnd = pxWidth; + } + + S32 pxBottom = (S32)(text_bottom + mGLFont->getDescenderHeight()); + + gGL.color4ub(255, 0, 0, 200); + while (pxStart + 1 < pxEnd) + { + gl_line_2d(pxStart, pxBottom, pxStart + 2, pxBottom - 2); + if (pxStart + 3 < pxEnd) + { + gl_line_2d(pxStart + 2, pxBottom - 3, pxStart + 4, pxBottom - 1); + } + pxStart += 4; + } + } + } + + // If we're editing... + if( hasFocus()) + { + //mBorder->setVisible(true); // ok, programmer art just this once. + // (Flash the cursor every half second) + if (!mReadOnly && gFocusMgr.getAppHasFocus()) + { + F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); + if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) + { + S32 cursor_left = findPixelNearestPos(); + cursor_left -= lineeditor_cursor_thickness / 2; + S32 cursor_right = cursor_left + lineeditor_cursor_thickness; + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) + { + const LLWString space(utf8str_to_wstring(std::string(" "))); + S32 wswidth = mGLFont->getWidth(space.c_str()); + S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1; + cursor_right = cursor_left + llmax(wswidth, width); + } + // Use same color as text for the Cursor + gl_rect_2d(cursor_left, cursor_top, + cursor_right, cursor_bottom, text_color); + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) + { + LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); + mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom, + tmp_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + 1); + } + + // Make sure the IME is in the right place + S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position + LLRect screen_pos = calcScreenRect(); + LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - lineeditor_v_pad ); + + ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]); + ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]); + getWindow()->setLanguageTextInput( ime_pos ); + } + } + + //draw label if no text is provided + //but we should draw it in a different color + //to give indication that it is not text you typed in + if (0 == mText.length() && (mReadOnly || mShowLabelFocused)) + { + mGLFont->render(mLabel.getWString(), 0, + mTextLeftEdge, (F32)text_bottom, + label_color, + LLFontGL::LEFT, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right, false); + } + + + // Draw children (border) + //mBorder->setVisible(true); + mBorder->setKeyboardFocusHighlight( true ); + LLView::draw(); + mBorder->setKeyboardFocusHighlight( false ); + //mBorder->setVisible(false); + } + else // does not have keyboard input + { + // draw label if no text provided + if (0 == mText.length()) + { + mGLFont->render(mLabel.getWString(), 0, + mTextLeftEdge, (F32)text_bottom, + label_color, + LLFontGL::LEFT, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + // Draw children (border) + LLView::draw(); + } + + if (mDrawAsterixes) + { + mText = saved_text; + } +} + + +// Returns the local screen space X coordinate associated with the text cursor position. +S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const +{ + S32 dpos = getCursor() - mScrollHPos + cursor_offset; + S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge; + return result; +} + +S32 LLLineEditor::calcCursorPos(S32 mouse_x) +{ + const llwchar* wtext = mText.getWString().c_str(); + LLWString asterix_text; + if (mDrawAsterixes) + { + for (S32 i = 0; i < mText.length(); i++) + { + asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK); + } + wtext = asterix_text.c_str(); + } + + S32 cur_pos = mScrollHPos + + mGLFont->charFromPixelOffset( + wtext, mScrollHPos, + (F32)(mouse_x - mTextLeftEdge), + (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive + + return cur_pos; +} +//virtual +void LLLineEditor::clear() +{ + mText.clear(); + setCursor(0); +} + +//virtual +void LLLineEditor::onTabInto() +{ + selectAll(); + LLUICtrl::onTabInto(); +} + +//virtual +bool LLLineEditor::acceptsTextInput() const +{ + return true; +} + +// Start or stop the editor from accepting text-editing keystrokes +void LLLineEditor::setFocus( bool new_state ) +{ + bool old_state = hasFocus(); + + if (!new_state) + { + getWindow()->allowLanguageTextInput(this, false); + } + + + // getting focus when we didn't have it before, and we want to select all + if (!old_state && new_state && mSelectAllonFocusReceived) + { + selectAll(); + // We don't want handleMouseUp() to "finish" the selection (and thereby + // set mSelectionEnd to where the mouse is), so we finish the selection + // here. + mIsSelecting = false; + } + + if( new_state ) + { + gEditMenuHandler = this; + + // Don't start the cursor flashing right away + mKeystrokeTimer.reset(); + } + else + { + // Not really needed, since loss of keyboard focus should take care of this, + // but limited paranoia is ok. + if( gEditMenuHandler == this ) + { + gEditMenuHandler = NULL; + } + + endSelection(); + } + + LLUICtrl::setFocus( new_state ); + + if (new_state) + { + // Allow Language Text Input only when this LineEditor has + // no prevalidate function attached. This criterion works + // fine on 1.15.0.2, since all prevalidate func reject any + // non-ASCII characters. I'm not sure on future versions, + // however. + getWindow()->allowLanguageTextInput(this, !mPrevalidator); + } +} + +//virtual +void LLLineEditor::setRect(const LLRect& rect) +{ + LLUICtrl::setRect(rect); + if (mBorder) + { + LLRect border_rect = mBorder->getRect(); + // Scalable UI somehow made these rectangles off-by-one. + // I don't know why. JC + border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom, + rect.getWidth()-1, rect.getHeight()-1); + mBorder->setRect(border_rect); + } +} + +void LLLineEditor::setPrevalidate(LLTextValidate::Validator validator) +{ + mPrevalidator = validator; + updateAllowingLanguageInput(); +} + +void LLLineEditor::setPrevalidateInput(LLTextValidate::Validator validator) +{ + mInputPrevalidator = validator; + updateAllowingLanguageInput(); +} + +bool LLLineEditor::prevalidateInput(const LLWString& wstr) +{ + return mInputPrevalidator.validate(wstr); +} + +// static +bool LLLineEditor::postvalidateFloat(const std::string &str) +{ + LLLocale locale(LLLocale::USER_LOCALE); + + bool success = true; + bool has_decimal = false; + bool has_digit = false; + + LLWString trimmed = utf8str_to_wstring(str); + LLWStringUtil::trim(trimmed); + S32 len = trimmed.length(); + if( 0 < len ) + { + S32 i = 0; + + // First character can be a negative sign + if( '-' == trimmed[0] ) + { + i++; + } + + // May be a comma or period, depending on the locale + llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint(); + + for( ; i < len; i++ ) + { + if( decimal_point == trimmed[i] ) + { + if( has_decimal ) + { + // can't have two + success = false; + break; + } + else + { + has_decimal = true; + } + } + else + if( LLStringOps::isDigit( trimmed[i] ) ) + { + has_digit = true; + } + else + { + success = false; + break; + } + } + } + + // Gotta have at least one + success = has_digit; + + return success; +} + +bool LLLineEditor::evaluateFloat() +{ + bool success; + F32 result = 0.f; + std::string expr = getText(); + LLStringUtil::toUpper(expr); + + success = LLCalc::getInstance()->evalString(expr, result); + + if (!success) + { + // Move the cursor to near the error on failure + setCursor(LLCalc::getInstance()->getLastErrorPos()); + // *TODO: Translated error message indicating the type of error? Select error text? + } + else + { + // Replace the expression with the result + std::string result_str = llformat("%f",result); + setText(result_str); + selectAll(); + } + + return success; +} + +void LLLineEditor::onMouseCaptureLost() +{ + endSelection(); +} + + +void LLLineEditor::setSelectAllonFocusReceived(bool b) +{ + mSelectAllonFocusReceived = b; +} + +void LLLineEditor::onKeystroke() +{ + if (mKeystrokeCallback) + { + mKeystrokeCallback(this); + } + + mSpellCheckStart = mSpellCheckEnd = -1; +} + +void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data) +{ + mKeystrokeCallback = boost::bind(callback, _1, user_data); +} + + +bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) +{ + mText.setArg(key, text); + return true; +} + +bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + mLabel.setArg(key, text); + return true; +} + + +void LLLineEditor::updateAllowingLanguageInput() +{ + // Allow Language Text Input only when this LineEditor has + // no prevalidate function attached (as long as other criteria + // common to LLTextEditor). This criterion works + // fine on 1.15.0.2, since all prevalidate func reject any + // non-ASCII characters. I'm not sure on future versions, + // however... + LLWindow* window = getWindow(); + if (!window) + { + // test app, no window available + return; + } + if (hasFocus() && !mReadOnly && !mDrawAsterixes && !mPrevalidator) + { + window->allowLanguageTextInput(this, true); + } + else + { + window->allowLanguageTextInput(this, false); + } +} + +bool LLLineEditor::hasPreeditString() const +{ + return (mPreeditPositions.size() > 1); +} + +void LLLineEditor::resetPreedit() +{ + if (hasSelection()) + { + if (hasPreeditString()) + { + LL_WARNS() << "Preedit and selection!" << LL_ENDL; + deselect(); + } + else + { + deleteSelection(); + } + } + if (hasPreeditString()) + { + const S32 preedit_pos = mPreeditPositions.front(); + mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos); + mText.insert(preedit_pos, mPreeditOverwrittenWString); + setCursor(preedit_pos); + + mPreeditWString.clear(); + mPreeditOverwrittenWString.clear(); + mPreeditPositions.clear(); + + // Don't reset key stroke timer nor invoke keystroke callback, + // because a call to updatePreedit should be follow soon in + // normal course of operation, and timer and callback will be + // maintained there. Doing so here made an odd sound. (VWR-3410) + } +} + +void LLLineEditor::updatePreedit(const LLWString &preedit_string, + const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) +{ + // Just in case. + if (mReadOnly) + { + return; + } + + // Note that call to updatePreedit is always preceeded by resetPreedit, + // so we have no existing selection/preedit. + + S32 insert_preedit_at = getCursor(); + + mPreeditWString = preedit_string; + mPreeditPositions.resize(preedit_segment_lengths.size() + 1); + S32 position = insert_preedit_at; + for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) + { + mPreeditPositions[i] = position; + position += preedit_segment_lengths[i]; + } + mPreeditPositions.back() = position; + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) ); + mText.erase(insert_preedit_at, mPreeditWString.length()); + } + else + { + mPreeditOverwrittenWString.clear(); + } + mText.insert(insert_preedit_at, mPreeditWString); + + mPreeditStandouts = preedit_standouts; + + setCursor(position); + setCursor(mPreeditPositions.front() + caret_position); + + // Update of the preedit should be caused by some key strokes. + mKeystrokeTimer.reset(); + onKeystroke(); + + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); +} + +bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +{ + if (control) + { + LLRect control_rect_screen; + localRectToScreen(getRect(), &control_rect_screen); + LLUI::getInstance()->screenRectToGL(control_rect_screen, control); + } + + S32 preedit_left_column, preedit_right_column; + if (hasPreeditString()) + { + preedit_left_column = mPreeditPositions.front(); + preedit_right_column = mPreeditPositions.back(); + } + else + { + preedit_left_column = preedit_right_column = getCursor(); + } + if (preedit_right_column < mScrollHPos) + { + // This should not occure... + return false; + } + + const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor()); + if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column) + { + return false; + } + + if (coord) + { + S32 query_local = findPixelNearestPos(query - getCursor()); + S32 query_screen_x, query_screen_y; + localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y); + LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); + } + + if (bounds) + { + S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor()); + S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness); + if (preedit_left_local > preedit_right_local) + { + // Is this condition possible? + preedit_right_local = preedit_left_local; + } + + LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0); + LLRect preedit_rect_screen; + localRectToScreen(preedit_rect_local, &preedit_rect_screen); + LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); + } + + return true; +} + +void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const +{ + if (hasPreeditString()) + { + *position = mPreeditPositions.front(); + *length = mPreeditPositions.back() - mPreeditPositions.front(); + } + else + { + *position = mCursorPos; + *length = 0; + } +} + +void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const +{ + if (hasSelection()) + { + *position = llmin(mSelectionStart, mSelectionEnd); + *length = llabs(mSelectionStart - mSelectionEnd); + } + else + { + *position = mCursorPos; + *length = 0; + } +} + +void LLLineEditor::markAsPreedit(S32 position, S32 length) +{ + deselect(); + setCursor(position); + if (hasPreeditString()) + { + LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL; + } + mPreeditWString.assign( LLWString( mText.getWString(), position, length ) ); + if (length > 0) + { + mPreeditPositions.resize(2); + mPreeditPositions[0] = position; + mPreeditPositions[1] = position + length; + mPreeditStandouts.resize(1); + mPreeditStandouts[0] = false; + } + else + { + mPreeditPositions.clear(); + mPreeditStandouts.clear(); + } + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + mPreeditOverwrittenWString = mPreeditWString; + } + else + { + mPreeditOverwrittenWString.clear(); + } +} + +S32 LLLineEditor::getPreeditFontSize() const +{ + return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); +} + +void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace) +{ + mReplaceNewlinesWithSpaces = replace; +} + +LLWString LLLineEditor::getConvertedText() const +{ + LLWString text = getWText(); + LLWStringUtil::trim(text); + if (!mReplaceNewlinesWithSpaces) + { + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + } + return text; +} + +void LLLineEditor::showContextMenu(S32 x, S32 y) +{ + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (!menu) + { + llassert(LLMenuGL::sMenuContainer != NULL); + menu = LLUICtrlFactory::createFromFile + ("menu_text_editor.xml", + LLMenuGL::sMenuContainer, + LLMenuHolderGL::child_registry_t::instance()); + setContextMenu(menu); + } + + if (menu) + { + gEditMenuHandler = this; + + S32 screen_x, screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + setCursorAtLocalPos(x); + if (hasSelection()) + { + if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) ) + { + deselect(); + } + else + { + setCursor(llmax(mSelectionStart, mSelectionEnd)); + } + } + + bool use_spellcheck = getSpellCheck(), is_misspelled = false; + if (use_spellcheck) + { + mSuggestionList.clear(); + + // If the cursor is on a misspelled word, retrieve suggestions for it + std::string misspelled_word = getMisspelledWord(mCursorPos); + if ((is_misspelled = !misspelled_word.empty())) + { + LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); + } + } + + menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty())); + menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled)); + menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled)); + menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled)); + menu->show(screen_x, screen_y, this); + } +} + +void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu) +{ + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (menu) + { + menu->die(); + mContextMenuHandle.markDead(); + } + + if (new_context_menu) + { + mContextMenuHandle = new_context_menu->getHandle(); + } +} + +void LLLineEditor::setFont(const LLFontGL* font) +{ + mGLFont = font; +} diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index debc19f7a6..e955cbb17d 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -1,472 +1,472 @@ -/** - * @file lllineeditor.h - * @brief Text editor widget to let users enter/edit a single line. - * - * Features: - * Text entry of a single line (text, delete, left and right arrow, insert, return). - * Callbacks either on every keystroke or just on the return key. - * Focus (allow multiple text entry widgets) - * Clipboard (cut, copy, and paste) - * Horizontal scrolling to allow strings longer than widget size allows - * Pre-validation (limit which keys can be used) - * Optional line history so previous entries can be recalled by CTRL UP/DOWN - * - * $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$ - */ - -#ifndef LL_LLLINEEDITOR_H -#define LL_LLLINEEDITOR_H - -#include "v4color.h" -#include "llframetimer.h" - -#include "lleditmenuhandler.h" -#include "llspellcheckmenuhandler.h" -#include "lluictrl.h" -#include "lluiimage.h" -#include "lluistring.h" -#include "llviewborder.h" - -#include "llpreeditor.h" -#include "lltextvalidate.h" - -class LLFontGL; -class LLLineEditorRollback; -class LLButton; -class LLContextMenu; - -class LLLineEditor -: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor, public LLSpellCheckMenuHandler -{ -public: - - typedef boost::function keystroke_callback_t; - - struct MaxLength : public LLInitParam::ChoiceBlock - { - Alternative bytes, chars; - - MaxLength() : bytes("max_length_bytes", 254), - chars("max_length_chars", 0) - {} - }; - - struct Params : public LLInitParam::Block - { - Optional default_text; - Optional max_length; - Optional keystroke_callback; - - Optional prevalidator; - Optional input_prevalidator; - - Optional border; - - Optional background_image, - background_image_disabled, - background_image_focused; - - Optional select_on_focus, - revert_on_esc, - spellcheck, - commit_on_focus_lost, - ignore_tab, - bg_image_always_focused, - show_label_focused, - is_password, - allow_emoji, - use_bg_color; - - // colors - Optional cursor_color, - bg_color, - text_color, - text_readonly_color, - text_tentative_color, - highlight_color, - preedit_bg_color; - - Optional text_pad_left, - text_pad_right; - - Ignored bg_visible; - - Params(); - }; - - void initFromParams(const LLLineEditor::Params& params); - -protected: - LLLineEditor(const Params&); - friend class LLUICtrlFactory; - friend class LLFloaterEditUI; - void showContextMenu(S32 x, S32 y); - -public: - virtual ~LLLineEditor(); - - // mousehandler overrides - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleDoubleClick(S32 x,S32 y,MASK mask) override; - /*virtual*/ bool handleMiddleMouseDown(S32 x,S32 y,MASK mask) override; - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override; - /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char) override; - /*virtual*/ void onMouseCaptureLost() override; - - // LLEditMenuHandler overrides - /*virtual*/ void cut() override; - /*virtual*/ bool canCut() const override; - /*virtual*/ void copy() override; - /*virtual*/ bool canCopy() const override; - /*virtual*/ void paste() override; - /*virtual*/ bool canPaste() const override; - - virtual void updatePrimary(); - virtual void copyPrimary(); - virtual void pastePrimary(); - virtual bool canPastePrimary() const; - - /*virtual*/ void doDelete() override; - /*virtual*/ bool canDoDelete() const override; - - /*virtual*/ void selectAll() override; - /*virtual*/ bool canSelectAll() const override; - - /*virtual*/ void deselect() override; - /*virtual*/ bool canDeselect() const override; - - // LLSpellCheckMenuHandler overrides - /*virtual*/ bool getSpellCheck() const override; - - /*virtual*/ const std::string& getSuggestion(U32 index) const override; - /*virtual*/ U32 getSuggestionCount() const override; - /*virtual*/ void replaceWithSuggestion(U32 index) override; - - /*virtual*/ void addToDictionary() override; - /*virtual*/ bool canAddToDictionary() const override; - - /*virtual*/ void addToIgnore() override; - /*virtual*/ bool canAddToIgnore() const override; - - // Spell checking helper functions - std::string getMisspelledWord(U32 pos) const; - bool isMisspelledWord(U32 pos) const; - void onSpellCheckSettingsChange(); - - // view overrides - /*virtual*/ void draw() override; - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override; - /*virtual*/ void onFocusReceived() override; - /*virtual*/ void onFocusLost() override; - /*virtual*/ void setEnabled(bool enabled) override; - - // UI control overrides - /*virtual*/ void clear() override; - /*virtual*/ void onTabInto() override; - /*virtual*/ void setFocus(bool b) override; - /*virtual*/ void setRect(const LLRect& rect) override; - /*virtual*/ bool acceptsTextInput() const override; - /*virtual*/ void onCommit() override; - /*virtual*/ bool isDirty() const override; // Returns true if user changed value at all - /*virtual*/ void resetDirty() override; // Clear dirty state - - // assumes UTF8 text - /*virtual*/ void setValue(const LLSD& value) override; - /*virtual*/ LLSD getValue() const override; - /*virtual*/ bool setTextArg(const std::string& key, const LLStringExplicit& text) override; - /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override; - - void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; } - const std::string& getLabel() { return mLabel.getString(); } - - void setText(const LLStringExplicit &new_text); - - const std::string& getText() const override { return mText.getString(); } - LLWString getWText() const { return mText.getWString(); } - LLWString getConvertedText() const; // trimmed text with paragraphs converted to newlines - - S32 getLength() const { return mText.length(); } - - S32 getCursor() const { return mCursorPos; } - void setCursor( S32 pos ); - void setCursorToEnd(); - - // set scroll to earliest position it can reasonable set - void resetScrollPosition(); - - // Selects characters 'start' to 'end'. - void setSelection(S32 start, S32 end); - /*virtual*/ void getSelectionRange(S32 *position, S32 *length) const override; - - void setCommitOnFocusLost( bool b ) { mCommitOnFocusLost = b; } - void setRevertOnEsc( bool b ) { mRevertOnEsc = b; } - void setKeystrokeOnEsc(bool b) { mKeystrokeOnEsc = b; } - - void setCursorColor(const LLColor4& c) { mCursorColor = c; } - const LLColor4& getCursorColor() const { return mCursorColor.get(); } - - void setFgColor( const LLColor4& c ) { mFgColor = c; } - void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; } - void setTentativeFgColor(const LLColor4& c) { mTentativeFgColor = c; } - - const LLColor4& getFgColor() const { return mFgColor.get(); } - const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor.get(); } - const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); } - - const LLFontGL* getFont() const override { return mGLFont; } - void setFont(const LLFontGL* font); - - void setIgnoreArrowKeys(bool b) { mIgnoreArrowKeys = b; } - void setIgnoreTab(bool b) { mIgnoreTab = b; } - void setPassDelete(bool b) { mPassDelete = b; } - void setAllowEmoji(bool b) { mAllowEmoji = b; } - void setDrawAsterixes(bool b); - - // get the cursor position of the beginning/end of the prev/next word in the text - S32 prevWordPos(S32 cursorPos) const; - S32 nextWordPos(S32 cursorPos) const; - - bool hasSelection() const { return (mSelectionStart != mSelectionEnd); } - void startSelection(); - void endSelection(); - void extendSelection(S32 new_cursor_pos); - void deleteSelection(); - - void setSelectAllonFocusReceived(bool b); - void setSelectAllonCommit(bool b) { mSelectAllonCommit = b; } - - void onKeystroke(); - typedef boost::function callback_t; - void setKeystrokeCallback(callback_t callback, void* user_data); - - void setMaxTextLength(S32 max_text_length); - void setMaxTextChars(S32 max_text_chars); - // Manipulate left and right padding for text - void getTextPadding(S32 *left, S32 *right); - void setTextPadding(S32 left, S32 right); - - // Prevalidation controls which keystrokes can affect the editor - void setPrevalidate(LLTextValidate::Validator validator); - // This method sets callback that prevents from: - // - deleting, selecting, typing, cutting, pasting characters that are not valid. - // Also callback that this method sets differs from setPrevalidate in a way that it validates just inputed - // symbols, before existing text is modified, but setPrevalidate validates line after it was modified. - void setPrevalidateInput(LLTextValidate::Validator validator); - static bool postvalidateFloat(const std::string &str); - - bool prevalidateInput(const LLWString& wstr); - bool evaluateFloat(); - - // line history support: - void setEnableLineHistory( bool enabled ) { mHaveHistory = enabled; } // switches line history on or off - void updateHistory(); // stores current line in history - - void setReplaceNewlinesWithSpaces(bool replace); - - void resetContextMenu() { setContextMenu(NULL); }; - - void setBgImage(LLPointer image) { mBgImage = image; } - void setBgImageFocused(LLPointer image) { mBgImageFocused = image; } - - void setShowContextMenu(bool show) { mShowContextMenu = show; } - bool getShowContextMenu() const { return mShowContextMenu; } - - private: - // private helper methods - - void pasteHelper(bool is_primary); - - void removeChar(); - void addChar(const llwchar c); - void setCursorAtLocalPos(S32 local_mouse_x); - S32 findPixelNearestPos(S32 cursor_offset = 0) const; - S32 calcCursorPos(S32 mouse_x); - bool handleSpecialKey(KEY key, MASK mask); - bool handleSelectionKey(KEY key, MASK mask); - bool handleControlKey(KEY key, MASK mask); - S32 handleCommitKey(KEY key, MASK mask); - void updateTextPadding(); - - // Draw the background image depending on enabled/focused state. - void drawBackground(); - - // - // private data members - // - void updateAllowingLanguageInput(); - bool hasPreeditString() const; - // Implementation (overrides) of LLPreeditor - /*virtual*/ void resetPreedit() override; - /*virtual*/ void updatePreedit(const LLWString &preedit_string, - const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) override; - /*virtual*/ void markAsPreedit(S32 position, S32 length) override; - /*virtual*/ void getPreeditRange(S32 *position, S32 *length) const override; - /*virtual*/ bool getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const override; - /*virtual*/ S32 getPreeditFontSize() const override; - /*virtual*/ LLWString getPreeditString() const override { return getWText(); } - - void setText(const LLStringExplicit &new_text, bool use_size_limit); - - void setContextMenu(LLContextMenu* new_context_menu); - -protected: - 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 - - // line history support: - bool mHaveHistory; // flag for enabled line history - typedef std::vector line_history_t; - line_history_t mLineHistory; // line history storage - line_history_t::iterator mCurrentHistoryLine; // currently browsed history line - - LLViewBorder* mBorder; - const LLFontGL* mGLFont; - S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes - S32 mMaxLengthChars; // Maximum number of characters in the string - S32 mCursorPos; // I-beam is just after the mCursorPos-th character. - S32 mScrollHPos; // Horizontal offset from the start of mText. Used for scrolling. - LLFrameTimer mScrollTimer; - S32 mTextPadLeft; // Used to reserve space before the beginning of the text for children. - S32 mTextPadRight; // Used to reserve space after the end of the text for children. - S32 mTextLeftEdge; // Pixels, cached left edge of text based on left padding and width - S32 mTextRightEdge; // Pixels, cached right edge of text based on right padding and width - - bool mCommitOnFocusLost; - bool mRevertOnEsc; - bool mKeystrokeOnEsc; - - keystroke_callback_t mKeystrokeCallback; - - bool mIsSelecting; // Selection for clipboard operations - S32 mSelectionStart; - S32 mSelectionEnd; - S32 mLastSelectionX; - S32 mLastSelectionY; - S32 mLastSelectionStart; - S32 mLastSelectionEnd; - - bool mSpellCheck; - S32 mSpellCheckStart; - S32 mSpellCheckEnd; - LLTimer mSpellCheckTimer; - std::list > mMisspellRanges; - std::vector mSuggestionList; - - LLTextValidate::Validator mPrevalidator; - LLTextValidate::Validator mInputPrevalidator; - - LLFrameTimer mKeystrokeTimer; - LLTimer mTripleClickTimer; - - LLUIColor mCursorColor; - LLUIColor mBgColor; - LLUIColor mFgColor; - LLUIColor mReadOnlyFgColor; - LLUIColor mTentativeFgColor; - LLUIColor mHighlightColor; // background for selected text - LLUIColor mPreeditBgColor; // preedit marker background color - - S32 mBorderThickness; - - bool mIgnoreArrowKeys; - bool mIgnoreTab; - bool mDrawAsterixes; - - bool mSelectAllonFocusReceived; - bool mSelectAllonCommit; - bool mPassDelete; - - bool mReadOnly; - - bool mShowImageFocused; - bool mShowLabelFocused; - - bool mAllowEmoji; - bool mUseBgColor; - - LLWString mPreeditWString; - LLWString mPreeditOverwrittenWString; - std::vector mPreeditPositions; - LLPreeditor::standouts_t mPreeditStandouts; - - LLHandle mContextMenuHandle; - - bool mShowContextMenu; - -private: - // Instances that by default point to the statics but can be overidden in XML. - LLPointer mBgImage; - LLPointer mBgImageDisabled; - LLPointer mBgImageFocused; - - bool mReplaceNewlinesWithSpaces; // if false, will replace pasted newlines with paragraph symbol. - - // private helper class - class LLLineEditorRollback - { - public: - LLLineEditorRollback( LLLineEditor* ed ) - : - mCursorPos( ed->mCursorPos ), - mScrollHPos( ed->mScrollHPos ), - mIsSelecting( ed->mIsSelecting ), - mSelectionStart( ed->mSelectionStart ), - mSelectionEnd( ed->mSelectionEnd ) - { - mText = ed->getText(); - } - - void doRollback( LLLineEditor* ed ) - { - ed->mCursorPos = mCursorPos; - ed->mScrollHPos = mScrollHPos; - ed->mIsSelecting = mIsSelecting; - ed->mSelectionStart = mSelectionStart; - ed->mSelectionEnd = mSelectionEnd; - ed->mText = mText; - ed->mPrevText = mText; - } - - std::string getText() { return mText; } - - private: - std::string mText; - S32 mCursorPos; - S32 mScrollHPos; - bool mIsSelecting; - S32 mSelectionStart; - S32 mSelectionEnd; - }; // end class LLLineEditorRollback - -}; // end class LLLineEditor - -// Build time optimization, generate once in .cpp file -#ifndef LLLINEEDITOR_CPP -extern template class LLLineEditor* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -#endif // LL_LINEEDITOR_ +/** + * @file lllineeditor.h + * @brief Text editor widget to let users enter/edit a single line. + * + * Features: + * Text entry of a single line (text, delete, left and right arrow, insert, return). + * Callbacks either on every keystroke or just on the return key. + * Focus (allow multiple text entry widgets) + * Clipboard (cut, copy, and paste) + * Horizontal scrolling to allow strings longer than widget size allows + * Pre-validation (limit which keys can be used) + * Optional line history so previous entries can be recalled by CTRL UP/DOWN + * + * $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$ + */ + +#ifndef LL_LLLINEEDITOR_H +#define LL_LLLINEEDITOR_H + +#include "v4color.h" +#include "llframetimer.h" + +#include "lleditmenuhandler.h" +#include "llspellcheckmenuhandler.h" +#include "lluictrl.h" +#include "lluiimage.h" +#include "lluistring.h" +#include "llviewborder.h" + +#include "llpreeditor.h" +#include "lltextvalidate.h" + +class LLFontGL; +class LLLineEditorRollback; +class LLButton; +class LLContextMenu; + +class LLLineEditor +: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor, public LLSpellCheckMenuHandler +{ +public: + + typedef boost::function keystroke_callback_t; + + struct MaxLength : public LLInitParam::ChoiceBlock + { + Alternative bytes, chars; + + MaxLength() : bytes("max_length_bytes", 254), + chars("max_length_chars", 0) + {} + }; + + struct Params : public LLInitParam::Block + { + Optional default_text; + Optional max_length; + Optional keystroke_callback; + + Optional prevalidator; + Optional input_prevalidator; + + Optional border; + + Optional background_image, + background_image_disabled, + background_image_focused; + + Optional select_on_focus, + revert_on_esc, + spellcheck, + commit_on_focus_lost, + ignore_tab, + bg_image_always_focused, + show_label_focused, + is_password, + allow_emoji, + use_bg_color; + + // colors + Optional cursor_color, + bg_color, + text_color, + text_readonly_color, + text_tentative_color, + highlight_color, + preedit_bg_color; + + Optional text_pad_left, + text_pad_right; + + Ignored bg_visible; + + Params(); + }; + + void initFromParams(const LLLineEditor::Params& params); + +protected: + LLLineEditor(const Params&); + friend class LLUICtrlFactory; + friend class LLFloaterEditUI; + void showContextMenu(S32 x, S32 y); + +public: + virtual ~LLLineEditor(); + + // mousehandler overrides + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleDoubleClick(S32 x,S32 y,MASK mask) override; + /*virtual*/ bool handleMiddleMouseDown(S32 x,S32 y,MASK mask) override; + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override; + /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char) override; + /*virtual*/ void onMouseCaptureLost() override; + + // LLEditMenuHandler overrides + /*virtual*/ void cut() override; + /*virtual*/ bool canCut() const override; + /*virtual*/ void copy() override; + /*virtual*/ bool canCopy() const override; + /*virtual*/ void paste() override; + /*virtual*/ bool canPaste() const override; + + virtual void updatePrimary(); + virtual void copyPrimary(); + virtual void pastePrimary(); + virtual bool canPastePrimary() const; + + /*virtual*/ void doDelete() override; + /*virtual*/ bool canDoDelete() const override; + + /*virtual*/ void selectAll() override; + /*virtual*/ bool canSelectAll() const override; + + /*virtual*/ void deselect() override; + /*virtual*/ bool canDeselect() const override; + + // LLSpellCheckMenuHandler overrides + /*virtual*/ bool getSpellCheck() const override; + + /*virtual*/ const std::string& getSuggestion(U32 index) const override; + /*virtual*/ U32 getSuggestionCount() const override; + /*virtual*/ void replaceWithSuggestion(U32 index) override; + + /*virtual*/ void addToDictionary() override; + /*virtual*/ bool canAddToDictionary() const override; + + /*virtual*/ void addToIgnore() override; + /*virtual*/ bool canAddToIgnore() const override; + + // Spell checking helper functions + std::string getMisspelledWord(U32 pos) const; + bool isMisspelledWord(U32 pos) const; + void onSpellCheckSettingsChange(); + + // view overrides + /*virtual*/ void draw() override; + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override; + /*virtual*/ void onFocusReceived() override; + /*virtual*/ void onFocusLost() override; + /*virtual*/ void setEnabled(bool enabled) override; + + // UI control overrides + /*virtual*/ void clear() override; + /*virtual*/ void onTabInto() override; + /*virtual*/ void setFocus(bool b) override; + /*virtual*/ void setRect(const LLRect& rect) override; + /*virtual*/ bool acceptsTextInput() const override; + /*virtual*/ void onCommit() override; + /*virtual*/ bool isDirty() const override; // Returns true if user changed value at all + /*virtual*/ void resetDirty() override; // Clear dirty state + + // assumes UTF8 text + /*virtual*/ void setValue(const LLSD& value) override; + /*virtual*/ LLSD getValue() const override; + /*virtual*/ bool setTextArg(const std::string& key, const LLStringExplicit& text) override; + /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override; + + void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; } + const std::string& getLabel() { return mLabel.getString(); } + + void setText(const LLStringExplicit &new_text); + + const std::string& getText() const override { return mText.getString(); } + LLWString getWText() const { return mText.getWString(); } + LLWString getConvertedText() const; // trimmed text with paragraphs converted to newlines + + S32 getLength() const { return mText.length(); } + + S32 getCursor() const { return mCursorPos; } + void setCursor( S32 pos ); + void setCursorToEnd(); + + // set scroll to earliest position it can reasonable set + void resetScrollPosition(); + + // Selects characters 'start' to 'end'. + void setSelection(S32 start, S32 end); + /*virtual*/ void getSelectionRange(S32 *position, S32 *length) const override; + + void setCommitOnFocusLost( bool b ) { mCommitOnFocusLost = b; } + void setRevertOnEsc( bool b ) { mRevertOnEsc = b; } + void setKeystrokeOnEsc(bool b) { mKeystrokeOnEsc = b; } + + void setCursorColor(const LLColor4& c) { mCursorColor = c; } + const LLColor4& getCursorColor() const { return mCursorColor.get(); } + + void setFgColor( const LLColor4& c ) { mFgColor = c; } + void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; } + void setTentativeFgColor(const LLColor4& c) { mTentativeFgColor = c; } + + const LLColor4& getFgColor() const { return mFgColor.get(); } + const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor.get(); } + const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); } + + const LLFontGL* getFont() const override { return mGLFont; } + void setFont(const LLFontGL* font); + + void setIgnoreArrowKeys(bool b) { mIgnoreArrowKeys = b; } + void setIgnoreTab(bool b) { mIgnoreTab = b; } + void setPassDelete(bool b) { mPassDelete = b; } + void setAllowEmoji(bool b) { mAllowEmoji = b; } + void setDrawAsterixes(bool b); + + // get the cursor position of the beginning/end of the prev/next word in the text + S32 prevWordPos(S32 cursorPos) const; + S32 nextWordPos(S32 cursorPos) const; + + bool hasSelection() const { return (mSelectionStart != mSelectionEnd); } + void startSelection(); + void endSelection(); + void extendSelection(S32 new_cursor_pos); + void deleteSelection(); + + void setSelectAllonFocusReceived(bool b); + void setSelectAllonCommit(bool b) { mSelectAllonCommit = b; } + + void onKeystroke(); + typedef boost::function callback_t; + void setKeystrokeCallback(callback_t callback, void* user_data); + + void setMaxTextLength(S32 max_text_length); + void setMaxTextChars(S32 max_text_chars); + // Manipulate left and right padding for text + void getTextPadding(S32 *left, S32 *right); + void setTextPadding(S32 left, S32 right); + + // Prevalidation controls which keystrokes can affect the editor + void setPrevalidate(LLTextValidate::Validator validator); + // This method sets callback that prevents from: + // - deleting, selecting, typing, cutting, pasting characters that are not valid. + // Also callback that this method sets differs from setPrevalidate in a way that it validates just inputed + // symbols, before existing text is modified, but setPrevalidate validates line after it was modified. + void setPrevalidateInput(LLTextValidate::Validator validator); + static bool postvalidateFloat(const std::string &str); + + bool prevalidateInput(const LLWString& wstr); + bool evaluateFloat(); + + // line history support: + void setEnableLineHistory( bool enabled ) { mHaveHistory = enabled; } // switches line history on or off + void updateHistory(); // stores current line in history + + void setReplaceNewlinesWithSpaces(bool replace); + + void resetContextMenu() { setContextMenu(NULL); }; + + void setBgImage(LLPointer image) { mBgImage = image; } + void setBgImageFocused(LLPointer image) { mBgImageFocused = image; } + + void setShowContextMenu(bool show) { mShowContextMenu = show; } + bool getShowContextMenu() const { return mShowContextMenu; } + + private: + // private helper methods + + void pasteHelper(bool is_primary); + + void removeChar(); + void addChar(const llwchar c); + void setCursorAtLocalPos(S32 local_mouse_x); + S32 findPixelNearestPos(S32 cursor_offset = 0) const; + S32 calcCursorPos(S32 mouse_x); + bool handleSpecialKey(KEY key, MASK mask); + bool handleSelectionKey(KEY key, MASK mask); + bool handleControlKey(KEY key, MASK mask); + S32 handleCommitKey(KEY key, MASK mask); + void updateTextPadding(); + + // Draw the background image depending on enabled/focused state. + void drawBackground(); + + // + // private data members + // + void updateAllowingLanguageInput(); + bool hasPreeditString() const; + // Implementation (overrides) of LLPreeditor + /*virtual*/ void resetPreedit() override; + /*virtual*/ void updatePreedit(const LLWString &preedit_string, + const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) override; + /*virtual*/ void markAsPreedit(S32 position, S32 length) override; + /*virtual*/ void getPreeditRange(S32 *position, S32 *length) const override; + /*virtual*/ bool getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const override; + /*virtual*/ S32 getPreeditFontSize() const override; + /*virtual*/ LLWString getPreeditString() const override { return getWText(); } + + void setText(const LLStringExplicit &new_text, bool use_size_limit); + + void setContextMenu(LLContextMenu* new_context_menu); + +protected: + 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 + + // line history support: + bool mHaveHistory; // flag for enabled line history + typedef std::vector line_history_t; + line_history_t mLineHistory; // line history storage + line_history_t::iterator mCurrentHistoryLine; // currently browsed history line + + LLViewBorder* mBorder; + const LLFontGL* mGLFont; + S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes + S32 mMaxLengthChars; // Maximum number of characters in the string + S32 mCursorPos; // I-beam is just after the mCursorPos-th character. + S32 mScrollHPos; // Horizontal offset from the start of mText. Used for scrolling. + LLFrameTimer mScrollTimer; + S32 mTextPadLeft; // Used to reserve space before the beginning of the text for children. + S32 mTextPadRight; // Used to reserve space after the end of the text for children. + S32 mTextLeftEdge; // Pixels, cached left edge of text based on left padding and width + S32 mTextRightEdge; // Pixels, cached right edge of text based on right padding and width + + bool mCommitOnFocusLost; + bool mRevertOnEsc; + bool mKeystrokeOnEsc; + + keystroke_callback_t mKeystrokeCallback; + + bool mIsSelecting; // Selection for clipboard operations + S32 mSelectionStart; + S32 mSelectionEnd; + S32 mLastSelectionX; + S32 mLastSelectionY; + S32 mLastSelectionStart; + S32 mLastSelectionEnd; + + bool mSpellCheck; + S32 mSpellCheckStart; + S32 mSpellCheckEnd; + LLTimer mSpellCheckTimer; + std::list > mMisspellRanges; + std::vector mSuggestionList; + + LLTextValidate::Validator mPrevalidator; + LLTextValidate::Validator mInputPrevalidator; + + LLFrameTimer mKeystrokeTimer; + LLTimer mTripleClickTimer; + + LLUIColor mCursorColor; + LLUIColor mBgColor; + LLUIColor mFgColor; + LLUIColor mReadOnlyFgColor; + LLUIColor mTentativeFgColor; + LLUIColor mHighlightColor; // background for selected text + LLUIColor mPreeditBgColor; // preedit marker background color + + S32 mBorderThickness; + + bool mIgnoreArrowKeys; + bool mIgnoreTab; + bool mDrawAsterixes; + + bool mSelectAllonFocusReceived; + bool mSelectAllonCommit; + bool mPassDelete; + + bool mReadOnly; + + bool mShowImageFocused; + bool mShowLabelFocused; + + bool mAllowEmoji; + bool mUseBgColor; + + LLWString mPreeditWString; + LLWString mPreeditOverwrittenWString; + std::vector mPreeditPositions; + LLPreeditor::standouts_t mPreeditStandouts; + + LLHandle mContextMenuHandle; + + bool mShowContextMenu; + +private: + // Instances that by default point to the statics but can be overidden in XML. + LLPointer mBgImage; + LLPointer mBgImageDisabled; + LLPointer mBgImageFocused; + + bool mReplaceNewlinesWithSpaces; // if false, will replace pasted newlines with paragraph symbol. + + // private helper class + class LLLineEditorRollback + { + public: + LLLineEditorRollback( LLLineEditor* ed ) + : + mCursorPos( ed->mCursorPos ), + mScrollHPos( ed->mScrollHPos ), + mIsSelecting( ed->mIsSelecting ), + mSelectionStart( ed->mSelectionStart ), + mSelectionEnd( ed->mSelectionEnd ) + { + mText = ed->getText(); + } + + void doRollback( LLLineEditor* ed ) + { + ed->mCursorPos = mCursorPos; + ed->mScrollHPos = mScrollHPos; + ed->mIsSelecting = mIsSelecting; + ed->mSelectionStart = mSelectionStart; + ed->mSelectionEnd = mSelectionEnd; + ed->mText = mText; + ed->mPrevText = mText; + } + + std::string getText() { return mText; } + + private: + std::string mText; + S32 mCursorPos; + S32 mScrollHPos; + bool mIsSelecting; + S32 mSelectionStart; + S32 mSelectionEnd; + }; // end class LLLineEditorRollback + +}; // end class LLLineEditor + +// Build time optimization, generate once in .cpp file +#ifndef LLLINEEDITOR_CPP +extern template class LLLineEditor* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +#endif // LL_LINEEDITOR_ diff --git a/indra/llui/lllocalcliprect.cpp b/indra/llui/lllocalcliprect.cpp index 6508106a9c..0204946125 100644 --- a/indra/llui/lllocalcliprect.cpp +++ b/indra/llui/lllocalcliprect.cpp @@ -1,110 +1,110 @@ -/** -* @file lllocalcliprect.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 "linden_common.h" - -#include "lllocalcliprect.h" - -#include "llfontgl.h" -#include "llui.h" - -/*static*/ std::stack LLScreenClipRect::sClipRectStack; - - -LLScreenClipRect::LLScreenClipRect(const LLRect& rect, bool enabled) -: mScissorState(GL_SCISSOR_TEST), - mEnabled(enabled) -{ - if (mEnabled) - { - pushClipRect(rect); - mScissorState.setEnabled(!sClipRectStack.empty()); - updateScissorRegion(); - } -} - -LLScreenClipRect::~LLScreenClipRect() -{ - if (mEnabled) - { - popClipRect(); - updateScissorRegion(); - } -} - -//static -void LLScreenClipRect::pushClipRect(const LLRect& rect) -{ - LLRect combined_clip_rect = rect; - if (!sClipRectStack.empty()) - { - LLRect top = sClipRectStack.top(); - combined_clip_rect.intersectWith(top); - - if(combined_clip_rect.isEmpty()) - { - // avoid artifacts where zero area rects show up as lines - combined_clip_rect = LLRect::null; - } - } - sClipRectStack.push(combined_clip_rect); -} - -//static -void LLScreenClipRect::popClipRect() -{ - sClipRectStack.pop(); -} - -//static -void LLScreenClipRect::updateScissorRegion() -{ - if (sClipRectStack.empty()) return; - - // finish any deferred calls in the old clipping region - gGL.flush(); - - LLRect rect = sClipRectStack.top(); - stop_glerror(); - S32 x,y,w,h; - x = llfloor(rect.mLeft * LLUI::getScaleFactor().mV[VX]); - y = llfloor(rect.mBottom * LLUI::getScaleFactor().mV[VY]); - w = llmax(0, llceil(rect.getWidth() * LLUI::getScaleFactor().mV[VX])) + 1; - h = llmax(0, llceil(rect.getHeight() * LLUI::getScaleFactor().mV[VY])) + 1; - glScissor( x,y,w,h ); - stop_glerror(); -} - -//--------------------------------------------------------------------------- -// LLLocalClipRect -//--------------------------------------------------------------------------- -LLLocalClipRect::LLLocalClipRect(const LLRect& rect, bool enabled /* = true */) -: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX, - rect.mTop + LLFontGL::sCurOrigin.mY, - rect.mRight + LLFontGL::sCurOrigin.mX, - rect.mBottom + LLFontGL::sCurOrigin.mY), enabled) -{} - -LLLocalClipRect::~LLLocalClipRect() -{} +/** +* @file lllocalcliprect.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 "linden_common.h" + +#include "lllocalcliprect.h" + +#include "llfontgl.h" +#include "llui.h" + +/*static*/ std::stack LLScreenClipRect::sClipRectStack; + + +LLScreenClipRect::LLScreenClipRect(const LLRect& rect, bool enabled) +: mScissorState(GL_SCISSOR_TEST), + mEnabled(enabled) +{ + if (mEnabled) + { + pushClipRect(rect); + mScissorState.setEnabled(!sClipRectStack.empty()); + updateScissorRegion(); + } +} + +LLScreenClipRect::~LLScreenClipRect() +{ + if (mEnabled) + { + popClipRect(); + updateScissorRegion(); + } +} + +//static +void LLScreenClipRect::pushClipRect(const LLRect& rect) +{ + LLRect combined_clip_rect = rect; + if (!sClipRectStack.empty()) + { + LLRect top = sClipRectStack.top(); + combined_clip_rect.intersectWith(top); + + if(combined_clip_rect.isEmpty()) + { + // avoid artifacts where zero area rects show up as lines + combined_clip_rect = LLRect::null; + } + } + sClipRectStack.push(combined_clip_rect); +} + +//static +void LLScreenClipRect::popClipRect() +{ + sClipRectStack.pop(); +} + +//static +void LLScreenClipRect::updateScissorRegion() +{ + if (sClipRectStack.empty()) return; + + // finish any deferred calls in the old clipping region + gGL.flush(); + + LLRect rect = sClipRectStack.top(); + stop_glerror(); + S32 x,y,w,h; + x = llfloor(rect.mLeft * LLUI::getScaleFactor().mV[VX]); + y = llfloor(rect.mBottom * LLUI::getScaleFactor().mV[VY]); + w = llmax(0, llceil(rect.getWidth() * LLUI::getScaleFactor().mV[VX])) + 1; + h = llmax(0, llceil(rect.getHeight() * LLUI::getScaleFactor().mV[VY])) + 1; + glScissor( x,y,w,h ); + stop_glerror(); +} + +//--------------------------------------------------------------------------- +// LLLocalClipRect +//--------------------------------------------------------------------------- +LLLocalClipRect::LLLocalClipRect(const LLRect& rect, bool enabled /* = true */) +: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX, + rect.mTop + LLFontGL::sCurOrigin.mY, + rect.mRight + LLFontGL::sCurOrigin.mX, + rect.mBottom + LLFontGL::sCurOrigin.mY), enabled) +{} + +LLLocalClipRect::~LLLocalClipRect() +{} diff --git a/indra/llui/lllocalcliprect.h b/indra/llui/lllocalcliprect.h index c39087070e..2a858e6163 100644 --- a/indra/llui/lllocalcliprect.h +++ b/indra/llui/lllocalcliprect.h @@ -1,63 +1,63 @@ -/** -* @file lllocalcliprect.h -* -* $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 LLLOCALCLIPRECT_H -#define LLLOCALCLIPRECT_H - -#include "llgl.h" -#include "llrect.h" // can't forward declare, it's templated -#include - -// Clip rendering to a specific rectangle using GL scissor -// Just create one of these on the stack: -// { -// LLLocalClipRect(rect); -// draw(); -// } -class LLScreenClipRect -{ -public: - LLScreenClipRect(const LLRect& rect, bool enabled = true); - virtual ~LLScreenClipRect(); - -private: - static void pushClipRect(const LLRect& rect); - static void popClipRect(); - static void updateScissorRegion(); - -private: - LLGLState mScissorState; - bool mEnabled; - - static std::stack sClipRectStack; -}; - -class LLLocalClipRect : public LLScreenClipRect -{ -public: - LLLocalClipRect(const LLRect& rect, bool enabled = true); - ~LLLocalClipRect(); -}; - -#endif +/** +* @file lllocalcliprect.h +* +* $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 LLLOCALCLIPRECT_H +#define LLLOCALCLIPRECT_H + +#include "llgl.h" +#include "llrect.h" // can't forward declare, it's templated +#include + +// Clip rendering to a specific rectangle using GL scissor +// Just create one of these on the stack: +// { +// LLLocalClipRect(rect); +// draw(); +// } +class LLScreenClipRect +{ +public: + LLScreenClipRect(const LLRect& rect, bool enabled = true); + virtual ~LLScreenClipRect(); + +private: + static void pushClipRect(const LLRect& rect); + static void popClipRect(); + static void updateScissorRegion(); + +private: + LLGLState mScissorState; + bool mEnabled; + + static std::stack sClipRectStack; +}; + +class LLLocalClipRect : public LLScreenClipRect +{ +public: + LLLocalClipRect(const LLRect& rect, bool enabled = true); + ~LLLocalClipRect(); +}; + +#endif diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index e3cb35abc7..2f91dcb046 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -1,246 +1,246 @@ -/** - * @file llbutton.cpp - * @brief LLButton base class - * - * $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 "linden_common.h" - -#include "llmenubutton.h" - -// Linden library includes -#include "lltoggleablemenu.h" -#include "llstring.h" -#include "v4color.h" - -static LLDefaultChildRegistry::Register r("menu_button"); - -void LLMenuButton::MenuPositions::declareValues() -{ - declare("topleft", MP_TOP_LEFT); - declare("topright", MP_TOP_RIGHT); - declare("bottomleft", MP_BOTTOM_LEFT); - declare("bottomright", MP_BOTTOM_RIGHT); -} - -LLMenuButton::Params::Params() -: menu_filename("menu_filename"), - position("menu_position", MP_BOTTOM_LEFT) -{ - addSynonym(position, "position"); -} - - -LLMenuButton::LLMenuButton(const LLMenuButton::Params& p) -: LLButton(p), - mIsMenuShown(false), - mMenuPosition(p.position), - mOwnMenu(false) -{ - std::string menu_filename = p.menu_filename; - - setMenu(menu_filename, mMenuPosition); - updateMenuOrigin(); -} - -LLMenuButton::~LLMenuButton() -{ - cleanup(); -} - -boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb ) -{ - return LLUICtrl::setMouseDownCallback(cb); -} - -void LLMenuButton::hideMenu() -{ - LLToggleableMenu* menu = getMenu(); - if (menu) - { - menu->setVisible(false); - } -} - -LLToggleableMenu* LLMenuButton::getMenu() -{ - return dynamic_cast(mMenuHandle.get()); -} - -void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition position /*MP_TOP_LEFT*/) -{ - if (menu_filename.empty()) - { - return; - } - - llassert(LLMenuGL::sMenuContainer != NULL); - LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); - if (!menu) - { - LL_WARNS() << "Error loading menu_button menu" << LL_ENDL; - return; - } - - setMenu(menu, position, true); -} - -void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/, bool take_ownership /*false*/) -{ - if (!menu) return; - - cleanup(); // destroy the previous memnu if we own it - - mMenuHandle = menu->getHandle(); - mMenuPosition = position; - mOwnMenu = take_ownership; - - menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); -} - -bool LLMenuButton::handleKeyHere(KEY key, MASK mask ) -{ - if (!getMenu()) return false; - - if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) - { - // *HACK: We emit the mouse down signal to fire the callback bound to the - // menu emerging event before actually displaying the menu. See STORM-263. - LLUICtrl::handleMouseDown(-1, -1, MASK_NONE); - - toggleMenu(); - return true; - } - - LLToggleableMenu* menu = getMenu(); - if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) - { - menu->setVisible(false); - return true; - } - - return false; -} - -bool LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) -{ - LLButton::handleMouseDown(x, y, mask); - - toggleMenu(); - - return true; -} - -void LLMenuButton::toggleMenu() -{ - if (mValidateSignal && !(*mValidateSignal)(this, LLSD())) - { - return; - } - - LLToggleableMenu* menu = getMenu(); - if (!menu) return; - - // Store the button rectangle to toggle menu visibility if a mouse event - // occurred inside or outside the button rect. - menu->setButtonRect(this); - - if (!menu->toggleVisibility() && mIsMenuShown) - { - setForcePressedState(false); - mIsMenuShown = false; - } - else - { - menu->buildDrawLabels(); - menu->arrangeAndClear(); - menu->updateParent(LLMenuGL::sMenuContainer); - - updateMenuOrigin(); - - LLMenuGL::showPopup(getParent(), menu, mX, mY); - - setForcePressedState(true); - mIsMenuShown = true; - } -} - -void LLMenuButton::updateMenuOrigin() -{ - LLToggleableMenu* menu = getMenu(); - if (!menu) return; - - LLRect rect = getRect(); - - switch (mMenuPosition) - { - case MP_TOP_LEFT: - { - mX = rect.mLeft; - mY = rect.mTop + menu->getRect().getHeight(); - break; - } - case MP_TOP_RIGHT: - { - const LLRect& menu_rect = menu->getRect(); - mX = rect.mRight - menu_rect.getWidth(); - mY = rect.mTop + menu_rect.getHeight(); - break; - } - case MP_BOTTOM_LEFT: - { - mX = rect.mLeft; - mY = rect.mBottom; - break; - } - case MP_BOTTOM_RIGHT: - { - const LLRect& menu_rect = menu->getRect(); - mX = rect.mRight - menu_rect.getWidth(); - mY = rect.mBottom; - break; - } - } -} - -void LLMenuButton::onMenuVisibilityChange(const LLSD& param) -{ - bool new_visibility = param["visibility"].asBoolean(); - bool is_closed_by_button_click = param["closed_by_button_click"].asBoolean(); - - // Reset the button "pressed" state only if the menu is shown by this particular - // menu button (not any other control) and is not being closed by a click on the button. - if (!new_visibility && !is_closed_by_button_click && mIsMenuShown) - { - setForcePressedState(false); - mIsMenuShown = false; - } -} - -void LLMenuButton::cleanup() -{ - if (mMenuHandle.get() && mOwnMenu) - { - mMenuHandle.get()->die(); - } -} +/** + * @file llbutton.cpp + * @brief LLButton base class + * + * $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 "linden_common.h" + +#include "llmenubutton.h" + +// Linden library includes +#include "lltoggleablemenu.h" +#include "llstring.h" +#include "v4color.h" + +static LLDefaultChildRegistry::Register r("menu_button"); + +void LLMenuButton::MenuPositions::declareValues() +{ + declare("topleft", MP_TOP_LEFT); + declare("topright", MP_TOP_RIGHT); + declare("bottomleft", MP_BOTTOM_LEFT); + declare("bottomright", MP_BOTTOM_RIGHT); +} + +LLMenuButton::Params::Params() +: menu_filename("menu_filename"), + position("menu_position", MP_BOTTOM_LEFT) +{ + addSynonym(position, "position"); +} + + +LLMenuButton::LLMenuButton(const LLMenuButton::Params& p) +: LLButton(p), + mIsMenuShown(false), + mMenuPosition(p.position), + mOwnMenu(false) +{ + std::string menu_filename = p.menu_filename; + + setMenu(menu_filename, mMenuPosition); + updateMenuOrigin(); +} + +LLMenuButton::~LLMenuButton() +{ + cleanup(); +} + +boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb ) +{ + return LLUICtrl::setMouseDownCallback(cb); +} + +void LLMenuButton::hideMenu() +{ + LLToggleableMenu* menu = getMenu(); + if (menu) + { + menu->setVisible(false); + } +} + +LLToggleableMenu* LLMenuButton::getMenu() +{ + return dynamic_cast(mMenuHandle.get()); +} + +void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition position /*MP_TOP_LEFT*/) +{ + if (menu_filename.empty()) + { + return; + } + + llassert(LLMenuGL::sMenuContainer != NULL); + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (!menu) + { + LL_WARNS() << "Error loading menu_button menu" << LL_ENDL; + return; + } + + setMenu(menu, position, true); +} + +void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/, bool take_ownership /*false*/) +{ + if (!menu) return; + + cleanup(); // destroy the previous memnu if we own it + + mMenuHandle = menu->getHandle(); + mMenuPosition = position; + mOwnMenu = take_ownership; + + menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); +} + +bool LLMenuButton::handleKeyHere(KEY key, MASK mask ) +{ + if (!getMenu()) return false; + + if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) + { + // *HACK: We emit the mouse down signal to fire the callback bound to the + // menu emerging event before actually displaying the menu. See STORM-263. + LLUICtrl::handleMouseDown(-1, -1, MASK_NONE); + + toggleMenu(); + return true; + } + + LLToggleableMenu* menu = getMenu(); + if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) + { + menu->setVisible(false); + return true; + } + + return false; +} + +bool LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) +{ + LLButton::handleMouseDown(x, y, mask); + + toggleMenu(); + + return true; +} + +void LLMenuButton::toggleMenu() +{ + if (mValidateSignal && !(*mValidateSignal)(this, LLSD())) + { + return; + } + + LLToggleableMenu* menu = getMenu(); + if (!menu) return; + + // Store the button rectangle to toggle menu visibility if a mouse event + // occurred inside or outside the button rect. + menu->setButtonRect(this); + + if (!menu->toggleVisibility() && mIsMenuShown) + { + setForcePressedState(false); + mIsMenuShown = false; + } + else + { + menu->buildDrawLabels(); + menu->arrangeAndClear(); + menu->updateParent(LLMenuGL::sMenuContainer); + + updateMenuOrigin(); + + LLMenuGL::showPopup(getParent(), menu, mX, mY); + + setForcePressedState(true); + mIsMenuShown = true; + } +} + +void LLMenuButton::updateMenuOrigin() +{ + LLToggleableMenu* menu = getMenu(); + if (!menu) return; + + LLRect rect = getRect(); + + switch (mMenuPosition) + { + case MP_TOP_LEFT: + { + mX = rect.mLeft; + mY = rect.mTop + menu->getRect().getHeight(); + break; + } + case MP_TOP_RIGHT: + { + const LLRect& menu_rect = menu->getRect(); + mX = rect.mRight - menu_rect.getWidth(); + mY = rect.mTop + menu_rect.getHeight(); + break; + } + case MP_BOTTOM_LEFT: + { + mX = rect.mLeft; + mY = rect.mBottom; + break; + } + case MP_BOTTOM_RIGHT: + { + const LLRect& menu_rect = menu->getRect(); + mX = rect.mRight - menu_rect.getWidth(); + mY = rect.mBottom; + break; + } + } +} + +void LLMenuButton::onMenuVisibilityChange(const LLSD& param) +{ + bool new_visibility = param["visibility"].asBoolean(); + bool is_closed_by_button_click = param["closed_by_button_click"].asBoolean(); + + // Reset the button "pressed" state only if the menu is shown by this particular + // menu button (not any other control) and is not being closed by a click on the button. + if (!new_visibility && !is_closed_by_button_click && mIsMenuShown) + { + setForcePressedState(false); + mIsMenuShown = false; + } +} + +void LLMenuButton::cleanup() +{ + if (mMenuHandle.get() && mOwnMenu) + { + mMenuHandle.get()->die(); + } +} diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index 4b66d236b7..a77ae7dae7 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -1,101 +1,101 @@ -/** - * @file llbutton.h - * @brief Header for buttons - * - * $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$ - */ - -#ifndef LL_LLMENUBUTTON_H -#define LL_LLMENUBUTTON_H - -#include "llbutton.h" - -class LLToggleableMenu; - -class LLMenuButton -: public LLButton -{ - LOG_CLASS(LLMenuButton); - -public: - typedef enum e_menu_position - { - MP_TOP_LEFT, - MP_TOP_RIGHT, - MP_BOTTOM_LEFT, - MP_BOTTOM_RIGHT - } EMenuPosition; - - struct MenuPositions - : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - struct Params - : public LLInitParam::Block - { - // filename for it's toggleable menu - Optional menu_filename; - Optional position; - - Params(); - }; - - - - boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); - - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleKeyHere(KEY key, MASK mask ); - - void hideMenu(); - - LLToggleableMenu* getMenu(); - void setMenu(const std::string& menu_filename, EMenuPosition position = MP_TOP_LEFT); - void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT, bool take_ownership = false); - - void setMenuPosition(EMenuPosition position) { mMenuPosition = position; } - -protected: - friend class LLUICtrlFactory; - LLMenuButton(const Params&); - ~LLMenuButton(); - - void toggleMenu(); - void updateMenuOrigin(); - - void onMenuVisibilityChange(const LLSD& param); - -private: - void cleanup(); - - LLHandle mMenuHandle; - bool mIsMenuShown; - EMenuPosition mMenuPosition; - S32 mX; - S32 mY; - bool mOwnMenu; // true if we manage the menu lifetime -}; - - -#endif // LL_LLMENUBUTTON_H +/** + * @file llbutton.h + * @brief Header for buttons + * + * $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$ + */ + +#ifndef LL_LLMENUBUTTON_H +#define LL_LLMENUBUTTON_H + +#include "llbutton.h" + +class LLToggleableMenu; + +class LLMenuButton +: public LLButton +{ + LOG_CLASS(LLMenuButton); + +public: + typedef enum e_menu_position + { + MP_TOP_LEFT, + MP_TOP_RIGHT, + MP_BOTTOM_LEFT, + MP_BOTTOM_RIGHT + } EMenuPosition; + + struct MenuPositions + : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + struct Params + : public LLInitParam::Block + { + // filename for it's toggleable menu + Optional menu_filename; + Optional position; + + Params(); + }; + + + + boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); + + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask ); + + void hideMenu(); + + LLToggleableMenu* getMenu(); + void setMenu(const std::string& menu_filename, EMenuPosition position = MP_TOP_LEFT); + void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT, bool take_ownership = false); + + void setMenuPosition(EMenuPosition position) { mMenuPosition = position; } + +protected: + friend class LLUICtrlFactory; + LLMenuButton(const Params&); + ~LLMenuButton(); + + void toggleMenu(); + void updateMenuOrigin(); + + void onMenuVisibilityChange(const LLSD& param); + +private: + void cleanup(); + + LLHandle mMenuHandle; + bool mIsMenuShown; + EMenuPosition mMenuPosition; + S32 mX; + S32 mY; + bool mOwnMenu; // true if we manage the menu lifetime +}; + + +#endif // LL_LLMENUBUTTON_H diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 9a058f420a..279f5628e1 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -1,4404 +1,4404 @@ -/** - * @file llmenugl.cpp - * @brief LLMenuItemGL base class - * - * $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$ - */ - -//***************************************************************************** -// -// This file contains the opengl based menu implementation. -// -// NOTES: A menu label is split into 4 columns. The left column, the -// label colum, the accelerator column, and the right column. The left -// column is used for displaying boolean values for toggle and check -// controls. The right column is used for submenus. -// -//***************************************************************************** - -//#include "llviewerprecompiledheaders.h" -#include "linden_common.h" - -#include "llmenugl.h" - -#include "llgl.h" -#include "llmath.h" -#include "llrender.h" -#include "llfocusmgr.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 "llstl.h" - -#include "v2math.h" -#include -#include - -// static -LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL; -view_listener_t::listener_map_t view_listener_t::sListeners; - -S32 MENU_BAR_HEIGHT = 18; -S32 MENU_BAR_WIDTH = 410; - -///============================================================================ -/// Local function declarations, constants, enums, and typedefs -///============================================================================ - -const S32 LABEL_BOTTOM_PAD_PIXELS = 2; - -const U32 LEFT_PAD_PIXELS = 3; -const U32 LEFT_WIDTH_PIXELS = 15; -const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS; - -const U32 RIGHT_PAD_PIXELS = 7; -const U32 RIGHT_WIDTH_PIXELS = 15; -const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; - -const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; - -const U32 BRIEF_PAD_PIXELS = 2; - -const U32 SEPARATOR_HEIGHT_PIXELS = 8; -const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10; -const S32 MENU_ITEM_PADDING = 4; - -const std::string SEPARATOR_NAME("separator"); -const std::string VERTICAL_SEPARATOR_LABEL( "|" ); - -const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK -const std::string LLMenuGL::BRANCH_SUFFIX( "\xe2\x96\xb8" ); // 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; - -bool LLMenuGL::sKeyboardMode = false; - -LLHandle LLMenuHolderGL::sItemLastSelectedHandle; -LLFrameTimer LLMenuHolderGL::sItemActivationTimer; - -const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f; - -static MenuRegistry::Register register_menu_item("menu_item"); -static MenuRegistry::Register register_separator("menu_item_separator"); -static MenuRegistry::Register register_menu_item_call("menu_item_call"); -static MenuRegistry::Register register_menu_item_check("menu_item_check"); -// Created programmatically but we need to specify custom colors in xml -static MenuRegistry::Register register_menu_item_tear_off("menu_item_tear_off"); -static MenuRegistry::Register register_menu("menu"); - -static LLDefaultChildRegistry::Register register_menu_default("menu"); - - - -///============================================================================ -/// Class LLMenuItemGL -///============================================================================ - -LLMenuItemGL::Params::Params() -: shortcut("shortcut"), - jump_key("jump_key", KEY_NONE), - use_mac_ctrl("use_mac_ctrl", false), - allow_key_repeat("allow_key_repeat", false), - rect("rect"), - left("left"), - top("top"), - right("right"), - bottom("bottom"), - width("width"), - height("height"), - bottom_delta("bottom_delta"), - left_delta("left_delta"), - enabled_color("enabled_color"), - disabled_color("disabled_color"), - highlight_bg_color("highlight_bg_color"), - highlight_fg_color("highlight_fg_color") -{ - changeDefault(mouse_opaque, true); -} - -// Default constructor -LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) -: LLUICtrl(p), - mJumpKey(p.jump_key), - mAllowKeyRepeat(p.allow_key_repeat), - mHighlight( false ), - mGotHover( false ), - mBriefItem( false ), - mDrawTextDisabled( false ), - mFont(p.font), - mAcceleratorKey(KEY_NONE), - mAcceleratorMask(MASK_NONE), - mLabel(p.label.isProvided() ? p.label() : p.name()), - mEnabledColor(p.enabled_color()), - mDisabledColor(p.disabled_color()), - mHighlightBackground(p.highlight_bg_color()), - mHighlightForeground(p.highlight_fg_color()) -{ -#ifdef LL_DARWIN - // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd - bool useMacCtrl = p.use_mac_ctrl; -#endif // LL_DARWIN - - std::string shortcut = p.shortcut; - if (shortcut.find("control") != shortcut.npos) - { -#ifdef LL_DARWIN - if ( useMacCtrl ) - { - mAcceleratorMask |= MASK_MAC_CONTROL; - } -#endif // LL_DARWIN - mAcceleratorMask |= MASK_CONTROL; - } - if (shortcut.find("alt") != shortcut.npos) - { - mAcceleratorMask |= MASK_ALT; - } - if (shortcut.find("shift") != shortcut.npos) - { - mAcceleratorMask |= MASK_SHIFT; - } - S32 pipe_pos = shortcut.rfind("|"); - std::string key_str = shortcut.substr(pipe_pos+1); - - LLKeyboard::keyFromString(key_str, &mAcceleratorKey); - - LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut - << ", key str: " << key_str - << ", accelerator mask: " << mAcceleratorMask - << ", accelerator key: " << mAcceleratorKey - << LL_ENDL; -} - -//virtual -void LLMenuItemGL::setValue(const LLSD& value) -{ - setLabel(value.asString()); -} - -//virtual -LLSD LLMenuItemGL::getValue() const -{ - return getLabel(); -} - -//virtual -bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const -{ - return (mAcceleratorKey == key) && (mAcceleratorMask == mask); -} - -//virtual -bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) -{ - if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) - { - onCommit(); - return true; - } - return false; -} - -bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) -{ - getWindow()->setCursor(UI_CURSOR_ARROW); - return true; -} - -//virtual -bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - return LLUICtrl::handleRightMouseDown(x,y,mask); -} - -void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask) -{ - setHover(true); - LLUICtrl::onMouseEnter(x,y,mask); -} - -void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask) -{ - setHover(false); - LLUICtrl::onMouseLeave(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) -{ - LLMenuKeyboardBinding *accelerator = NULL; - - if (mAcceleratorKey != KEY_NONE) - { - std::list::iterator list_it; - for (list_it = listp->begin(); list_it != listp->end(); ++list_it) - { - accelerator = *list_it; - if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS))) - { - - // *NOTE: get calling code to throw up warning or route - // warning messages back to app-provided output - // std::string warning; - // warning.append("Duplicate key binding <"); - // appendAcceleratorString( warning ); - // warning.append("> for menu items:\n "); - // warning.append(accelerator->mName); - // warning.append("\n "); - // warning.append(mLabel); - - // LL_WARNS() << warning << LL_ENDL; - // LLAlertDialog::modalAlert(warning); - return false; - } - } - if (!accelerator) - { - accelerator = new LLMenuKeyboardBinding; - if (accelerator) - { - accelerator->mKey = mAcceleratorKey; - accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS); -// accelerator->mName = mLabel; - } - listp->push_back(accelerator);//addData(accelerator); - } - } - return true; -} - -// This function appends the character string representation of -// the current accelerator key and mask to the provided string. -void LLMenuItemGL::appendAcceleratorString( std::string& st ) const -{ - st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey ); - LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL; -} - -void LLMenuItemGL::setJumpKey(KEY key) -{ - mJumpKey = LLStringOps::toUpper((char)key); -} - - -// virtual -U32 LLMenuItemGL::getNominalHeight( void ) const -{ - return 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() const -{ - return (LLMenuGL*) getParent(); -} - - -// getNominalWidth() - returns the normal width of this control in -// pixels - this is used for calculating the widest item, as well as -// for horizontal arrangement. -U32 LLMenuItemGL::getNominalWidth( void ) const -{ - U32 width; - - if (mBriefItem) - { - width = BRIEF_PAD_PIXELS; - } - else - { - width = PLAIN_PAD_PIXELS; - } - - if( KEY_NONE != mAcceleratorKey ) - { - width += getMenu()->getShortcutPad(); - std::string temp; - appendAcceleratorString( temp ); - width += mFont->getWidth( temp ); - } - width += mFont->getWidth( mLabel.getWString().c_str() ); - return width; -} - -// called to rebuild the draw label -void LLMenuItemGL::buildDrawLabel( void ) -{ - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel.getString(); - appendAcceleratorString( st ); - mDrawAccelLabel = st; -} - -void LLMenuItemGL::onCommit( void ) -{ - // 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()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - LLUICtrl::onCommit(); -} - -// set the hover status (called by it's menu) - void LLMenuItemGL::setHighlight( bool highlight ) -{ - if (highlight) - { - getMenu()->clearHoverItem(); - } - - if (mHighlight != highlight) - { - dirtyRect(); - } - - mHighlight = highlight; -} - - -bool LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) -{ - if (getHighlight() && - getMenu()->isOpen()) - { - if (key == KEY_UP) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - getMenu()->highlightPrevItem(this); - return true; - } - else if (key == KEY_DOWN) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - getMenu()->highlightNextItem(this); - return true; - } - else if (key == KEY_RETURN && mask == MASK_NONE) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - onCommit(); - return true; - } - } - - return false; -} - -bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask) -{ - // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(false); - - onCommit(); - make_ui_sound("UISndClickRelease"); - return LLView::handleMouseUp(x, y, mask); -} - -bool LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask) -{ - // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(false); - - setHighlight(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 !getMenu()->isScrollable(); -} - -void LLMenuItemGL::draw( void ) -{ - // *FIX: This can be optimized by using switches. Want to avoid - // that until the functionality is finalized. - - // HACK: Brief items don't highlight. Pie menu takes care of it. JC - // let disabled items be highlighted, just don't draw them as such - if( getEnabled() && getHighlight() && !mBriefItem) - { - gGL.color4fv( mHighlightBackground.get().mV ); - - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - } - - LLColor4 color; - - if ( getEnabled() && getHighlight() ) - { - color = mHighlightForeground.get(); - } - else if( getEnabled() && !mDrawTextDisabled ) - { - color = mEnabledColor.get(); - } - else - { - color = mDisabledColor.get(); - } - - // Highlight if needed - if( ll::ui::SearchableControl::getHighlighted() ) - color = ll::ui::SearchableControl::getHighlightColor(); - - // Draw the text on top. - if (mBriefItem) - { - mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL); - } - else - { - if( !mDrawBoolLabel.empty() ) - { - mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - } - mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - if( !mDrawAccelLabel.empty() ) - { - mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - } - if( !mDrawBranchLabel.empty() ) - { - mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - } - } - - // underline "jump" key only when keyboard navigation has been initiated - if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) - { - std::string upper_case_label = mLabel.getString(); - LLStringUtil::toUpper(upper_case_label); - std::string::size_type offset = upper_case_label.find(mJumpKey); - if (offset != std::string::npos) - { - S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset); - S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1); - gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1); - } - } -} - -bool LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - mLabel.setArg(key, text); - return true; -} - -void LLMenuItemGL::onVisibilityChange(bool new_visibility) -{ - if (getMenu()) - { - getMenu()->needsArrange(); - } - LLView::onVisibilityChange(new_visibility); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemSeparatorGL -// -// This class represents a separator. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLMenuItemSeparatorGL::Params::Params() - : on_visible("on_visible") -{ -} - -LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) : - LLMenuItemGL( p ) -{ - if (p.on_visible.isProvided()) - { - mVisibleSignal.connect(initEnableCallback(p.on_visible)); - } -} - -//virtual -U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const -{ - return SEPARATOR_HEIGHT_PIXELS; -} - -void LLMenuItemSeparatorGL::draw( void ) -{ - gGL.color4fv( mDisabledColor.get().mV ); - const S32 y = getRect().getHeight() / 2; - const S32 PAD = 6; - gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); -} - -void LLMenuItemSeparatorGL::buildDrawLabel( void ) -{ - if (mVisibleSignal.num_slots() > 0) - { - bool visible = mVisibleSignal(this, LLSD()); - setVisible(visible); - } -} - -bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) -{ - LLMenuGL* parent_menu = getMenu(); - if (y > getRect().getHeight() / 2) - { - // 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->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : false; - } - else - { - LLView* next_menu_item = parent_menu->findPrevSibling(this); - return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : false; - } -} - -bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) -{ - LLMenuGL* parent_menu = getMenu(); - if (y > getRect().getHeight() / 2) - { - LLView* prev_menu_item = parent_menu->findNextSibling(this); - return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : false; - } - else - { - LLView* next_menu_item = parent_menu->findPrevSibling(this); - return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : false; - } -} - -bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) -{ - LLMenuGL* parent_menu = getMenu(); - if (y > getRect().getHeight() / 2) - { - parent_menu->highlightPrevItem(this, false); - return false; - } - else - { - parent_menu->highlightNextItem(this, false); - return false; - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemVerticalSeparatorGL -// -// This class represents a vertical separator. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemVerticalSeparatorGL -: public LLMenuItemSeparatorGL -{ -public: - LLMenuItemVerticalSeparatorGL( void ); - - virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; } -}; - -LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) -{ - setLabel( VERTICAL_SEPARATOR_LABEL ); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemTearOffGL -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p) -: LLMenuItemGL(p) -{ -} - -// 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::onCommit() -{ - if (getMenu()->getTornOff()) - { - LLTearOffMenu * torn_off_menu = dynamic_cast(getMenu()->getParent()); - if (torn_off_menu) - { - torn_off_menu->closeFloater(); - } - } - else - { - // transfer keyboard focus and highlight to first real item in list - if (getHighlight()) - { - getMenu()->highlightNextItem(this); - } - - getMenu()->needsArrange(); - - LLFloater* parent_floater = getParentFloater(); - LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu()); - - if (tear_off_menu) - { - if (parent_floater) - { - parent_floater->addDependentFloater(tear_off_menu, false); - } - - // give focus to torn off menu because it will have - // been taken away when parent menu closes - tear_off_menu->setFocus(true); - } - } - LLMenuItemGL::onCommit(); -} - -void LLMenuItemTearOffGL::draw() -{ - // disabled items can be highlighted, but shouldn't render as such - if( getEnabled() && getHighlight() && !isBriefItem()) - { - gGL.color4fv( mHighlightBackground.get().mV ); - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - } - - if (getEnabled()) - { - gGL.color4fv( mEnabledColor.get().mV ); - } - else - { - gGL.color4fv( mDisabledColor.get().mV ); - } - const S32 y = getRect().getHeight() / 3; - const S32 PAD = 6; - gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); - gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 ); -} - -U32 LLMenuItemTearOffGL::getNominalHeight( void ) const -{ - return TEAROFF_SEPARATOR_HEIGHT_PIXELS; -} - -///============================================================================ -/// Class LLMenuItemCallGL -///============================================================================ - -LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p) -: LLMenuItemGL(p) -{ -} - -void LLMenuItemCallGL::initFromParams(const Params& p) -{ - if (p.on_visible.isProvided()) - { - mVisibleSignal.connect(initEnableCallback(p.on_visible)); - } - if (p.on_enable.isProvided()) - { - setEnableCallback(initEnableCallback(p.on_enable)); - // Set the enabled control variable (for backwards compatability) - if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty()) - { - LLControlVariable* control = findControl(p.on_enable.control_name()); - if (control) - { - setEnabledControlVariable(control); - } - else - { - LL_WARNS() << "Failed to assign 'enabled' control variable to menu " << getName() - << ": control " << p.on_enable.control_name() - << " does not exist." << LL_ENDL; - } - } - } - if (p.on_click.isProvided()) - { - setCommitCallback(initCommitCallback(p.on_click)); - } - - LLUICtrl::initFromParams(p); -} - -void LLMenuItemCallGL::onCommit( void ) -{ - // RN: menu item can be deleted in callback, so beware - getMenu()->setItemLastSelected( this ); - - LLMenuItemGL::onCommit(); -} - -void LLMenuItemCallGL::updateEnabled( void ) -{ - if (mEnableSignal.num_slots() > 0) - { - bool enabled = mEnableSignal(this, LLSD()); - if (mEnabledControlVariable) - { - if (!enabled) - { - // callback overrides control variable; this will call setEnabled() - mEnabledControlVariable->set(false); - } - } - else - { - setEnabled(enabled); - } - } -} - -void LLMenuItemCallGL::updateVisible( void ) -{ - if (mVisibleSignal.num_slots() > 0) - { - bool visible = mVisibleSignal(this, LLSD()); - setVisible(visible); - } -} - -void LLMenuItemCallGL::buildDrawLabel( void ) -{ - updateEnabled(); - updateVisible(); - 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)) ) - { - updateEnabled(); - if (getEnabled()) - { - onCommit(); - return true; - } - } - return false; -} - -// handleRightMouseUp moved into base class LLMenuItemGL so clicks are -// handled for all menu item types - -///============================================================================ -/// Class LLMenuItemCheckGL -///============================================================================ -LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p) -: LLMenuItemCallGL(p) -{ -} - -void LLMenuItemCheckGL::initFromParams(const Params& p) -{ - if (p.on_check.isProvided()) - { - setCheckCallback(initEnableCallback(p.on_check)); - // Set the control name (for backwards compatability) - if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty()) - { - setControlName(p.on_check.control_name()); - } - } - - LLMenuItemCallGL::initFromParams(p); -} - -void LLMenuItemCheckGL::onCommit( void ) -{ - LLMenuItemCallGL::onCommit(); -} - -//virtual -void LLMenuItemCheckGL::setValue(const LLSD& value) -{ - LLUICtrl::setValue(value); - if(value.asBoolean()) - { - mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; - } - else - { - mDrawBoolLabel.clear(); - } -} - -//virtual -LLSD LLMenuItemCheckGL::getValue() const -{ - // Get our boolean value from the view model. - // If we don't override this method then the implementation from - // LLMenuItemGL will return a string. (EXT-8501) - return LLUICtrl::getValue(); -} - -// called to rebuild the draw label -void LLMenuItemCheckGL::buildDrawLabel( void ) -{ - // Note: mCheckSignal() returns true if no callbacks are set - bool checked = mCheckSignal(this, LLSD()); - if (mControlVariable) - { - if (!checked) - setControlValue(false); // callback overrides control variable; this will call setValue() - } - else - { - setValue(checked); - } - if(getValue().asBoolean()) - { - mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; - } - else - { - mDrawBoolLabel.clear(); - } - LLMenuItemCallGL::buildDrawLabel(); -} - -///============================================================================ -/// Class LLMenuItemBranchGL -///============================================================================ -LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p) - : LLMenuItemGL(p) -{ - LLMenuGL* branch = p.branch; - if (branch) - { - mBranchHandle = branch->getHandle(); - branch->setVisible(false); - branch->setParentMenuItem(this); - } -} - -LLMenuItemBranchGL::~LLMenuItemBranchGL() -{ - if (mBranchHandle.get()) - { - mBranchHandle.get()->die(); - } -} - - - -// virtual -LLView* LLMenuItemBranchGL::getChildView(const std::string& name, bool recurse) const -{ - LLMenuGL* branch = getBranch(); - if (branch) - { - if (branch->getName() == name) - { - return branch; - } - - // Always recurse on branches - return branch->getChildView(name, recurse); - } - - return LLView::getChildView(name, recurse); -} - -LLView* LLMenuItemBranchGL::findChildView(const std::string& name, bool recurse) const -{ - LLMenuGL* branch = getBranch(); - if (branch) - { - if (branch->getName() == name) - { - return branch; - } - - // Always recurse on branches - return branch->findChildView(name, recurse); - } - - return LLView::findChildView(name, recurse); -} - -// virtual -bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) -{ - // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(false); - - onCommit(); - make_ui_sound("UISndClickRelease"); - return true; -} - -bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const -{ - return getBranch() && getBranch()->hasAccelerator(key, mask); -} - -bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) -{ - return getBranch() && getBranch()->handleAcceleratorKey(key, mask); -} - -// This function checks to see if the accelerator key is already in use; -// if not, it will be added to the list -bool LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) -{ - LLMenuGL* branch = getBranch(); - if (!branch) - return false; - - U32 item_count = branch->getItemCount(); - LLMenuItemGL *item; - - while (item_count--) - { - if ((item = branch->getItem(item_count))) - { - return item->addToAcceleratorList(listp); - } - } - - return false; -} - - -// called to rebuild the draw label -void LLMenuItemBranchGL::buildDrawLabel( void ) -{ - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel; - appendAcceleratorString( st ); - mDrawAccelLabel = st; - mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; -} - -void LLMenuItemBranchGL::onCommit( void ) -{ - openMenu(); - - // keyboard navigation automatically propagates highlight to sub-menu - // to facilitate fast menu control via jump keys - if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem()) - { - getBranch()->highlightNextItem(NULL); - } - - LLUICtrl::onCommit(); -} - -bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - bool handled = false; - if (getBranch() && called_from_parent) - { - handled = getBranch()->handleKey(key, mask, called_from_parent); - } - - if (!handled) - { - handled = LLMenuItemGL::handleKey(key, mask, called_from_parent); - } - - return handled; -} - -bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, bool called_from_parent) -{ - bool handled = false; - if (getBranch() && called_from_parent) - { - handled = getBranch()->handleUnicodeChar(uni_char, true); - } - - if (!handled) - { - handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent); - } - - return handled; -} - - -void LLMenuItemBranchGL::setHighlight( bool highlight ) -{ - if (highlight == getHighlight()) - return; - - 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 - LLFloater * menu_parent = dynamic_cast(getMenu()->getParent()); - if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus()) - { - auto_open = false; - } - // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus) - if (branch->getTornOff()) - { - auto_open = false; - } - LLMenuItemGL::setHighlight(highlight); - if( highlight ) - { - if(auto_open) - { - openMenu(); - } - } - else - { - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast(branch->getParent()); - if (branch_parent) - { - branch_parent->setFocus(false); - } - branch->clearHoverItem(); - } - else - { - branch->setVisible( false ); - } - } -} - -void LLMenuItemBranchGL::draw() -{ - LLMenuItemGL::draw(); - if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff()) - { - setHighlight(true); - } -} - -void LLMenuItemBranchGL::updateBranchParent(LLView* parentp) -{ - if (getBranch() && getBranch()->getParent() == NULL) - { - // make the branch menu a sibling of my parent menu - getBranch()->updateParent(parentp); - } -} - -void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility) -{ - if (!new_visibility && getBranch() && !getBranch()->getTornOff()) - { - getBranch()->setVisible(false); - } - LLMenuItemGL::onVisibilityChange(new_visibility); -} - -bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask) -{ - 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())) - { - if (branch->getVisible() && key == KEY_LEFT) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - bool handled = branch->clearHoverItem(); - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast(branch->getParent()); - if (branch_parent) - { - branch_parent->setFocus(false); - } - } - if (handled && getMenu()->getTornOff()) - { - LLFloater * menu_parent = dynamic_cast(getMenu()->getParent()); - if (menu_parent) - { - menu_parent->setFocus(true); - } - } - return handled; - } - - if (key == KEY_RIGHT && !branch->getHighlightedItem()) - { - // 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() -{ - LLMenuGL* branch = getBranch(); - if (!branch) - return; - - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast(branch->getParent()); - if (branch_parent) - { - gFloaterView->bringToFront(branch_parent); - // this might not be necessary, as torn off branches don't get focus and hence no highligth - branch->highlightNextItem(NULL); - } - } - else if( !branch->getVisible() ) - { - // get valid rectangle for menus - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); - - branch->arrange(); - - 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, branch->getParent()); - - branch_rect.setLeftTopAndSize( left, top, - branch_rect.getWidth(), branch_rect.getHeight() ); - - if (branch->getCanTearOff()) - { - branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); - } - 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; - branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); - if( y < menu_region_rect.mBottom ) - { - // open upwards if menu extends past bottom - // adjust by the height of the menu item branch since it is a submenu - if (y + 2 * branch_rect.getHeight() - getRect().getHeight() > menu_region_rect.mTop) - { - // overlaps with top border, align with top - delta_y = menu_region_rect.mTop - y - branch_rect.getHeight(); - } - else - { - delta_y = branch_rect.getHeight() - getRect().getHeight(); - } - } - - if( x + branch_rect.getWidth() > menu_region_rect.mRight ) - { - // move sub-menu over to left side - delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth()))); - } - branch->translate( delta_x, delta_y ); - - branch->setVisible( true ); - branch->getParent()->sendChildToFront(branch); - - dirtyRect(); - } -} - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemBranchDownGL -// -// The LLMenuItemBranchDownGL represents a menu item that has a -// sub-menu. This is used to make menu bar menus. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemBranchDownGL : public LLMenuItemBranchGL -{ -protected: - -public: - LLMenuItemBranchDownGL( const Params& ); - - // returns the normal width of this control in pixels - this is - // used for calculating the widest item, as well as for horizontal - // arrangement. - virtual U32 getNominalWidth( void ) const; - - // called to rebuild the draw label - virtual void buildDrawLabel( void ); - - // handles opening, positioning, and arranging the menu branch associated with this item - virtual void openMenu( void ); - - // set the hover status (called by it's menu) and if the object is - // active. This is used for behavior transfer. - virtual void setHighlight( bool highlight ); - - virtual bool isActive( void ) const; - - // LLView functionality - virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); - virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); - virtual void draw( void ); - virtual bool handleKeyHere(KEY key, MASK mask); - - virtual bool handleAcceleratorKey(KEY key, MASK mask); - - virtual void onFocusLost(); - virtual void setFocus(bool b); -}; - -LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) : - LLMenuItemBranchGL(p) -{ -} - -// returns the normal width of this control in pixels - this is used -// for calculating the widest item, as well as for horizontal -// arrangement. -U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const -{ - U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS; - width += getFont()->getWidth( mLabel.getWString().c_str() ); - return width; -} - -// called to rebuild the draw label -void LLMenuItemBranchDownGL::buildDrawLabel( void ) -{ - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel; - appendAcceleratorString( st ); - mDrawAccelLabel = st; -} - -void LLMenuItemBranchDownGL::openMenu( void ) -{ - LLMenuGL* branch = getBranch(); - if( branch->getVisible() && !branch->getTornOff() ) - { - branch->setVisible( false ); - } - else - { - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast(branch->getParent()); - if (branch_parent) - { - gFloaterView->bringToFront(branch_parent); - } - } - else - { - // We're showing the drop-down menu, so patch up its labels/rects - branch->arrange(); - - LLRect rect = branch->getRect(); - S32 left = 0; - S32 top = getRect().mBottom; - localPointToOtherView(left, top, &left, &top, branch->getParent()); - - rect.setLeftTopAndSize( left, top, - rect.getWidth(), rect.getHeight() ); - branch->setRect( rect ); - S32 x = 0; - S32 y = 0; - branch->localPointToScreen( 0, 0, &x, &y ); - S32 delta_x = 0; - - LLCoordScreen window_size; - LLWindow* windowp = getWindow(); - windowp->getSize(&window_size); - - S32 window_width = window_size.mX; - if( x > window_width - rect.getWidth() ) - { - delta_x = (window_width - rect.getWidth()) - x; - } - branch->translate( delta_x, 0 ); - - setHighlight(true); - branch->setVisible( true ); - branch->getParent()->sendChildToFront(branch); - } - } -} - -// set the hover status (called by it's menu) -void LLMenuItemBranchDownGL::setHighlight( bool highlight ) -{ - if (highlight == getHighlight()) - return; - - //NOTE: Purposely calling all the way to the base to bypass auto-open. - LLMenuItemGL::setHighlight(highlight); - - LLMenuGL* branch = getBranch(); - if (!branch) - return; - - if( !highlight) - { - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast(branch->getParent()); - if (branch_parent) - { - branch_parent->setFocus(false); - } - branch->clearHoverItem(); - } - else - { - branch->setVisible( false ); - } - } -} - -bool LLMenuItemBranchDownGL::isActive() const -{ - // for top level menus, being open is sufficient to be considered - // active, because clicking on them with the mouse will open - // them, without moving keyboard focus to them - return isOpen(); -} - -bool LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - // switch to mouse control mode - LLMenuGL::setKeyboardMode(false); - - if (getVisible() && isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - else - { - onCommit(); - } - - make_ui_sound("UISndClick"); - return true; -} - -bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask ) -{ - return true; -} - - -bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) -{ - bool branch_visible = getBranch()->getVisible(); - bool handled = getBranch()->handleAcceleratorKey(key, mask); - if (handled && !branch_visible && isInVisibleChain()) - { - // flash this menu entry because we triggered an invisible menu item - LLMenuHolderGL::setActivatedItem(this); - } - - return handled; -} -void LLMenuItemBranchDownGL::onFocusLost() -{ - // needed for tab-based selection - LLMenuItemBranchGL::onFocusLost(); - LLMenuGL::setKeyboardMode(false); - setHighlight(false); -} - -void LLMenuItemBranchDownGL::setFocus(bool b) -{ - // needed for tab-based selection - LLMenuItemBranchGL::setFocus(b); - LLMenuGL::setKeyboardMode(b); - setHighlight(b); -} - -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()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode())) - { - if (key == KEY_LEFT) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this); - // open new menu only if previous menu was open - if (itemp && itemp->getEnabled() && menu_open) - { - itemp->onCommit(); - } - - return true; - } - else if (key == KEY_RIGHT) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - LLMenuItemGL* itemp = getMenu()->highlightNextItem(this); - // open new menu only if previous menu was open - if (itemp && itemp->getEnabled() && menu_open) - { - itemp->onCommit(); - } - - return true; - } - else if (key == KEY_DOWN) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - if (!isActive()) - { - onCommit(); - } - getBranch()->highlightNextItem(NULL); - return true; - } - else if (key == KEY_UP) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - if (!isActive()) - { - onCommit(); - } - getBranch()->highlightPrevItem(NULL); - return true; - } - } - - return false; -} - -void LLMenuItemBranchDownGL::draw( void ) -{ - //FIXME: try removing this - if (getBranch()->getVisible() && !getBranch()->getTornOff()) - { - setHighlight(true); - } - - if( getHighlight() ) - { - gGL.color4fv( mHighlightBackground.get().mV ); - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - } - - LLColor4 color; - if (getHighlight()) - { - color = mHighlightForeground.get(); - } - else if( getEnabled() ) - { - color = mEnabledColor.get(); - } - else - { - color = mDisabledColor.get(); - } - getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color, - LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL); - - - // underline navigation key only when keyboard navigation has been initiated - if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) - { - std::string upper_case_label = mLabel.getString(); - LLStringUtil::toUpper(upper_case_label); - std::string::size_type offset = upper_case_label.find(getJumpKey()); - if (offset != std::string::npos) - { - S32 x_offset = ll_round((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f); - S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset); - S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1); - gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS); - } - } -} - - -class LLMenuScrollItem : public LLMenuItemCallGL -{ -public: - enum EArrowType - { - ARROW_DOWN, - ARROW_UP - }; - struct ArrowTypes : public LLInitParam::TypeValuesHelper - { - static void declareValues() - { - declare("up", ARROW_UP); - declare("down", ARROW_DOWN); - } - }; - - struct Params : public LLInitParam::Block - { - Optional arrow_type; - Optional scroll_callback; - }; - -protected: - LLMenuScrollItem(const Params&); - friend class LLUICtrlFactory; - -public: - /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent); - /*virtual*/ void setEnabled(bool enabled); - virtual void onCommit( void ); - -private: - LLButton* mArrowBtn; -}; - -LLMenuScrollItem::LLMenuScrollItem(const Params& p) -: LLMenuItemCallGL(p) -{ - std::string icon; - if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP) - { - icon = "arrow_up.tga"; - } - else - { - icon = "arrow_down.tga"; - } - - LLButton::Params bparams; - - // Disabled the Return key handling by LLMenuScrollItem instead of - // passing the key press to the currently selected menu item. See STORM-385. - bparams.commit_on_return(false); - bparams.mouse_opaque(true); - bparams.scale_image(false); - bparams.click_callback(p.scroll_callback); - bparams.mouse_held_callback(p.scroll_callback); - bparams.follows.flags(FOLLOWS_ALL); - std::string background = "transparent.j2c"; - bparams.image_unselected.name(background); - bparams.image_disabled.name(background); - bparams.image_selected.name(background); - bparams.image_hover_selected.name(background); - bparams.image_disabled_selected.name(background); - bparams.image_hover_unselected.name(background); - bparams.image_overlay.name(icon); - - mArrowBtn = LLUICtrlFactory::create(bparams); - addChild(mArrowBtn); -} - -/*virtual*/ -void LLMenuScrollItem::draw() -{ - LLUICtrl::draw(); -} - -/*virtual*/ -void LLMenuScrollItem::reshape(S32 width, S32 height, bool called_from_parent) -{ - mArrowBtn->reshape(width, height, called_from_parent); - LLView::reshape(width, height, called_from_parent); -} - -/*virtual*/ -void LLMenuScrollItem::setEnabled(bool enabled) -{ - mArrowBtn->setEnabled(enabled); - LLView::setEnabled(enabled); -} - -void LLMenuScrollItem::onCommit( void ) -{ - LLUICtrl::onCommit(); -} - -///============================================================================ -/// Class LLMenuGL -///============================================================================ - -LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) -: LLUICtrl(p), - mBackgroundColor( p.bg_color() ), - mBgVisible( p.bg_visible ), - mDropShadowed( p.drop_shadow ), - mHasSelection(false), - mHorizontalLayout( p.horizontal_layout ), - mScrollable(mHorizontalLayout ? false : p.scrollable), // Scrolling is supported only for vertical layout - mMaxScrollableItems(p.max_scrollable_items), - mPreferredWidth(p.preferred_width), - mKeepFixedSize( p.keep_fixed_size ), - mLabel (p.label), - mLastMouseX(0), - mLastMouseY(0), - mMouseVelX(0), - mMouseVelY(0), - mTornOff(false), - mTearOffItem(NULL), - mSpilloverBranch(NULL), - mFirstVisibleItem(NULL), - mArrowUpItem(NULL), - mArrowDownItem(NULL), - mSpilloverMenu(NULL), - mJumpKey(p.jump_key), - mCreateJumpKeys(p.create_jump_keys), - mNeedsArrange(false), - mAlwaysShowMenu(false), - mResetScrollPositionOnShow(true), - mShortcutPad(p.shortcut_pad), - mFont(p.font) -{ - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("_"); - tokenizer tokens(p.label(), sep); - tokenizer::iterator token_iter; - - S32 token_count = 0; - std::string new_menu_label; - for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - new_menu_label += (*token_iter); - if (token_count > 0) - { - setJumpKey((*token_iter).c_str()[0]); - } - ++token_count; - } - setLabel(new_menu_label); - - mFadeTimer.stop(); -} - -void LLMenuGL::initFromParams(const LLMenuGL::Params& p) -{ - LLUICtrl::initFromParams(p); - setCanTearOff(p.can_tear_off); -} - -// Destroys the object -LLMenuGL::~LLMenuGL( void ) -{ - // delete the branch, as it might not be in view hierarchy - // leave the menu, because it is always in view hierarchy - delete mSpilloverBranch; - mJumpKeys.clear(); -} - -void LLMenuGL::setCanTearOff(bool tear_off) -{ - if (tear_off && mTearOffItem == NULL) - { - LLMenuItemTearOffGL::Params p; - mTearOffItem = LLUICtrlFactory::create(p); - addChild(mTearOffItem); - } - else if (!tear_off && mTearOffItem != NULL) - { - mItems.remove(mTearOffItem); - removeChild(mTearOffItem); - delete mTearOffItem; - mTearOffItem = NULL; - needsArrange(); - } -} - -bool LLMenuGL::addChild(LLView* view, S32 tab_group) -{ - LLMenuGL* menup = dynamic_cast(view); - if (menup) - { - return appendMenu(menup); - } - - LLMenuItemGL* itemp = dynamic_cast(view); - if (itemp) - { - return append(itemp); - } - - return false; -} - -// Used in LLContextMenu and in LLTogleableMenu - -// Add an item to the context menu branch -bool LLMenuGL::addContextChild(LLView* view, S32 tab_group) -{ - LLContextMenu* context = dynamic_cast(view); - if (context) - { - return appendContextSubMenu(context); - } - - LLMenuItemSeparatorGL* separator = dynamic_cast(view); - if (separator) - { - return append(separator); - } - - LLMenuItemGL* item = dynamic_cast(view); - if (item) - { - return append(item); - } - - LLMenuGL* menup = dynamic_cast(view); - if (menup) - { - return appendMenu(menup); - } - - return false; -} - - -void LLMenuGL::deleteAllChildren() -{ - mItems.clear(); - LLUICtrl::deleteAllChildren(); -} - -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); -} - -bool LLMenuGL::postBuild() -{ - createJumpKeys(); - return LLUICtrl::postBuild(); -} - -// 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() -{ - LLMenuItemGL* highlighted_item = getHighlightedItem(); - bool active = getVisible() && getEnabled(); - - if (active) - { - if (getTornOff()) - { - // activation of jump keys on torn off menus controlled by keyboard focus - LLFloater * parent = dynamic_cast(getParent()); - if (parent) - { - active = parent->hasFocus(); - } - } - else - { - // Are we the terminal active menu? - // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus) - // and we don't have a highlighted menu item pointing to an active sub-menu - active = (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active... - && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active - } - } - - return active; -} - -bool LLMenuGL::isOpen() -{ - if (getTornOff()) - { - LLMenuItemGL* itemp = getHighlightedItem(); - // if we have an open sub-menu, then we are considered part of - // the open menu chain even if we don't have focus - if (itemp && itemp->isOpen()) - { - return true; - } - // otherwise we are only active if we have keyboard focus - LLFloater * parent = dynamic_cast(getParent()); - if (parent) - { - return parent->hasFocus(); - } - return false; - } - else - { - // normally, menus are hidden as soon as the user focuses - // on another menu, so just use the visibility criterion - return getVisible(); - } -} - - - -bool LLMenuGL::scrollItems(EScrollingDirection direction) -{ - // Slowing down items scrolling when arrow button is held - if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem) - { - mScrollItemsTimer.setTimerExpirySec(.033f); - } - else - { - return false; - } - - switch (direction) - { - case SD_UP: - { - item_list_t::iterator cur_item_iter; - item_list_t::iterator prev_item_iter; - for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) - { - if( (*cur_item_iter) == mFirstVisibleItem) - { - break; - } - if ((*cur_item_iter)->getVisible()) - { - prev_item_iter = cur_item_iter; - } - } - - if ((*prev_item_iter)->getVisible()) - { - mFirstVisibleItem = *prev_item_iter; - } - break; - } - case SD_DOWN: - { - if (NULL == mFirstVisibleItem) - { - mFirstVisibleItem = *mItems.begin(); - } - - 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) == mFirstVisibleItem) - { - break; - } - } - - item_list_t::iterator next_item_iter; - - if (cur_item_iter != mItems.end()) - { - for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) - { - if( (*next_item_iter)->getVisible()) - { - break; - } - } - - if (next_item_iter != mItems.end() && - (*next_item_iter)->getVisible()) - { - mFirstVisibleItem = *next_item_iter; - } - } - break; - } - case SD_BEGIN: - { - mFirstVisibleItem = *mItems.begin(); - break; - } - case SD_END: - { - item_list_t::reverse_iterator first_visible_item_iter = mItems.rend(); - - // Need to scroll through number of actual existing items in menu. - // Otherwise viewer will hang for a time needed to scroll U32_MAX - // times in std::advance(). STORM-659. - size_t nitems = mItems.size(); - U32 scrollable_items = nitems < mMaxScrollableItems ? nitems : mMaxScrollableItems; - - // Advance by mMaxScrollableItems back from the end of the list - // to make the last item visible. - std::advance(first_visible_item_iter, scrollable_items); - mFirstVisibleItem = *first_visible_item_iter; - break; - } - default: - LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL; - } - - mNeedsArrange = true; - arrangeAndClear(); - - return true; -} - -// rearrange the child rects so they fit the shape of the menu. -void LLMenuGL::arrange( void ) -{ - // calculate the height & width, and set our rect based on that - // information. - const LLRect& initial_rect = getRect(); - - U32 width = 0, height = MENU_ITEM_PADDING; - - cleanupSpilloverBranch(); - - if( mItems.size() ) - { - const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0); - - // 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(); - - // *FIX: create the item first and then ask for its dimensions? - S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate - S32 spillover_item_height = 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 != mSpilloverBranch - && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width) - { - // no room for any more items - createSpilloverBranch(); - - 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->addChild(itemp); - } - - addChild(mSpilloverBranch); - - height = llmax(height, mSpilloverBranch->getNominalHeight()); - width += mSpilloverBranch->getNominalWidth(); - - break; - } - else - { - // track our rect - height = llmax(height, (*item_iter)->getNominalHeight()); - width += (*item_iter)->getNominalWidth(); - } - } - } - } - else - { - for (LLMenuItemGL* itemp : mItems) - { - // do first so LLMenuGLItemCall can call on_visible to determine if visible - itemp->buildDrawLabel(); - } - item_list_t::iterator item_iter; - - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getVisible()) - { - if (!getTornOff() - && !mScrollable - && *item_iter != mSpilloverBranch - && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height) - { - // don't show only one item - int visible_items = 0; - item_list_t::iterator count_iter; - for (count_iter = item_iter; count_iter != mItems.end(); ++count_iter) - { - if((*count_iter)->getVisible()) - visible_items++; - } - if (visible_items>1) - { - // no room for any more items - createSpilloverBranch(); - - 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->addChild(itemp); - } - - - addChild(mSpilloverBranch); - - height += mSpilloverBranch->getNominalHeight(); - width = llmax( width, mSpilloverBranch->getNominalWidth() ); - - break; - } - } - - // track our rect - height += (*item_iter)->getNominalHeight(); - width = llmax( width, (*item_iter)->getNominalWidth() ); - - if (mScrollable) - { - // Determining visible items boundaries - if (NULL == mFirstVisibleItem) - { - mFirstVisibleItem = *item_iter; - } - - if (*item_iter == mFirstVisibleItem) - { - height_before_first_visible_item = height - (*item_iter)->getNominalHeight(); - first_visible_item_iter = item_iter; - scrollable_items_cnt = 0; - } - - if (-1 != height_before_first_visible_item && 0 == visible_items_height && - (++scrollable_items_cnt > mMaxScrollableItems || - height - height_before_first_visible_item > max_height - spillover_item_height * 2 )) - { - first_hidden_item_iter = item_iter; - visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight(); - scrollable_items_cnt--; - } - } - } - } - - if (mPreferredWidth < U32_MAX) - width = llmin(mPreferredWidth, max_width); - - if (mScrollable) - { - S32 max_items_height = max_height - spillover_item_height * 2; - - if (visible_items_height == 0) - visible_items_height = height - height_before_first_visible_item; - - // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit - if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems) - { - item_list_t::iterator tmp_iter(first_visible_item_iter); - while (visible_items_height < max_items_height && - scrollable_items_cnt < mMaxScrollableItems && - first_visible_item_iter != mItems.begin()) - { - if ((*first_visible_item_iter)->getVisible()) - { - // It keeps visible item, after first_visible_item_iter - tmp_iter = first_visible_item_iter; - } - - first_visible_item_iter--; - - if ((*first_visible_item_iter)->getVisible()) - { - visible_items_height += (*first_visible_item_iter)->getNominalHeight(); - height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight(); - scrollable_items_cnt++; - } - } - - // Roll back one item, that doesn't fit - if (visible_items_height > max_items_height) - { - visible_items_height -= (*first_visible_item_iter)->getNominalHeight(); - height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight(); - scrollable_items_cnt--; - first_visible_item_iter = tmp_iter; - } - if (!(*first_visible_item_iter)->getVisible()) - { - first_visible_item_iter = tmp_iter; - } - - mFirstVisibleItem = *first_visible_item_iter; - } - } - } - - S32 cur_height = (S32)llmin(max_height, height); - - if (mScrollable && - (height_before_first_visible_item > MENU_ITEM_PADDING || - height_before_first_visible_item + visible_items_height < (S32)height)) - { - // Reserving 2 extra slots for arrow items - cur_height = visible_items_height + spillover_item_height * 2; - } - - setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height)); - - S32 cur_width = 0; - S32 offset = 0; - if (mScrollable) - { - // No space for all items, creating arrow items - if (height_before_first_visible_item > MENU_ITEM_PADDING || - height_before_first_visible_item + visible_items_height < (S32)height) - { - if (NULL == mArrowUpItem) - { - LLMenuScrollItem::Params item_params; - item_params.name(ARROW_UP); - item_params.arrow_type(LLMenuScrollItem::ARROW_UP); - item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP)); - - mArrowUpItem = LLUICtrlFactory::create(item_params); - LLUICtrl::addChild(mArrowUpItem); - - } - if (NULL == mArrowDownItem) - { - LLMenuScrollItem::Params item_params; - item_params.name(ARROW_DOWN); - item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN); - item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN)); - - mArrowDownItem = LLUICtrlFactory::create(item_params); - LLUICtrl::addChild(mArrowDownItem); - } - - LLRect rect; - mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight())); - mArrowUpItem->setVisible(true); - mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING); - mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight()); - mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight())); - mArrowDownItem->setVisible(true); - mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height); - mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight()); - - cur_height -= mArrowUpItem->getNominalHeight(); - - offset = menu_region_rect.mRight; // This moves items behind visible area - } - else - { - if (NULL != mArrowUpItem) - { - mArrowUpItem->setVisible(false); - } - if (NULL != mArrowDownItem) - { - mArrowDownItem->setVisible(false); - } - } - - } - - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getVisible()) - { - if (mScrollable) - { - if (item_iter == first_visible_item_iter) - { - offset = 0; - } - else if (item_iter == first_hidden_item_iter) - { - offset = menu_region_rect.mRight; // This moves items behind visible area - } - } - - // setup item rect to hold label - LLRect rect; - if (mHorizontalLayout) - { - rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height); - cur_width += (*item_iter)->getNominalWidth(); - } - else - { - rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight()); - if (offset == 0) - { - cur_height -= (*item_iter)->getNominalHeight(); - } - } - (*item_iter)->setRect( rect ); - } - } - - - if (getTornOff()) - { - LLTearOffMenu * torn_off_menu = dynamic_cast(getParent()); - if (torn_off_menu) - { - torn_off_menu->updateSize(); - } - } - } - if (mKeepFixedSize) - { - reshape(initial_rect.getWidth(), initial_rect.getHeight()); - } -} - -void LLMenuGL::arrangeAndClear( void ) -{ - if (mNeedsArrange) - { - arrange(); - mNeedsArrange = false; - } -} - -void LLMenuGL::createSpilloverBranch() -{ - if (!mSpilloverBranch) - { - // should be NULL but delete anyway - delete mSpilloverMenu; - // technically, you can't tear off spillover menus, but we're passing the handle - // along just to be safe - LLMenuGL::Params p; - std::string label = LLTrans::getString("More"); - p.name("More"); - p.label(label); - p.bg_color(mBackgroundColor); - p.bg_visible(true); - p.can_tear_off(false); - mSpilloverMenu = new LLMenuGL(p); - mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer); - - LLMenuItemBranchGL::Params branch_params; - branch_params.name = "More"; - branch_params.label = label; - branch_params.branch = mSpilloverMenu; - branch_params.font.style = "italic"; - branch_params.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - branch_params.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - branch_params.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - - mSpilloverBranch = LLUICtrlFactory::create(branch_params); - } -} - -void LLMenuGL::cleanupSpilloverBranch() -{ - if (mSpilloverBranch && mSpilloverBranch->getParent() == this) - { - // head-recursion to propagate items back up to root menu - mSpilloverMenu->cleanupSpilloverBranch(); - - // pop off spillover items - while (mSpilloverMenu->getItemCount()) - { - LLMenuItemGL* itemp = mSpilloverMenu->getItem(0); - mSpilloverMenu->removeChild(itemp); - // put them at the end of our own list - addChild(itemp); - } - - // Delete the branch, and since the branch will delete the menu, - // set the menu* to null. - delete mSpilloverBranch; - mSpilloverBranch = NULL; - mSpilloverMenu = NULL; - } -} - -void LLMenuGL::createJumpKeys() -{ - if (!mCreateJumpKeys) return; - mCreateJumpKeys = false; - - mJumpKeys.clear(); - - std::set unique_words; - std::set shared_words; - - item_list_t::iterator item_it; - typedef boost::tokenizer > tokenizer; - boost::char_separator sep(" "); - - for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) - { - std::string uppercase_label = (*item_it)->getLabel(); - LLStringUtil::toUpper(uppercase_label); - - tokenizer tokens(uppercase_label, sep); - tokenizer::iterator token_iter; - for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - if (unique_words.find(*token_iter) != unique_words.end()) - { - // this word exists in more than one menu instance - shared_words.insert(*token_iter); - } - else - { - // we have a new word, keep track of it - unique_words.insert(*token_iter); - } - } - } - - // pre-assign specified jump keys - for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) - { - KEY jump_key = (*item_it)->getJumpKey(); - if(jump_key != KEY_NONE) - { - if (mJumpKeys.find(jump_key) == mJumpKeys.end()) - { - mJumpKeys.insert(std::pair(jump_key, (*item_it))); - } - else - { - // this key is already spoken for, - // so we need to reassign it below - (*item_it)->setJumpKey(KEY_NONE); - } - } - } - - for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) - { - // skip over items that already have assigned jump keys - if ((*item_it)->getJumpKey() != KEY_NONE) - { - continue; - } - std::string uppercase_label = (*item_it)->getLabel(); - LLStringUtil::toUpper(uppercase_label); - - tokenizer tokens(uppercase_label, sep); - tokenizer::iterator token_iter; - - bool found_key = false; - for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - std::string uppercase_word = *token_iter; - - // this word is not shared with other menu entries... - if (shared_words.find(*token_iter) == shared_words.end()) - { - S32 i; - for(i = 0; i < (S32)uppercase_word.size(); i++) - { - char jump_key = uppercase_word[i]; - - if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) && - mJumpKeys.find(jump_key) == mJumpKeys.end())) - { - mJumpKeys.insert(std::pair(jump_key, (*item_it))); - (*item_it)->setJumpKey(jump_key); - found_key = true; - break; - } - } - } - if (found_key) - { - break; - } - } - } -} - -// remove all items on the menu -void LLMenuGL::empty( void ) -{ - cleanupSpilloverBranch(); - - mItems.clear(); - mFirstVisibleItem = NULL; - mArrowUpItem = NULL; - mArrowDownItem = NULL; - - deleteAllChildren(); -} - -// erase group of items from menu -void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/) -{ - S32 items = mItems.size(); - - if ( items == 0 || begin >= end || begin < 0 || end > items ) - { - return; - } - - item_list_t::iterator start_position = mItems.begin(); - std::advance(start_position, begin); - - item_list_t::iterator end_position = mItems.begin(); - std::advance(end_position, end); - - for (item_list_t::iterator position_iter = start_position; position_iter != end_position; position_iter++) - { - LLUICtrl::removeChild(*position_iter); - } - - mItems.erase(start_position, end_position); - - if (arrange) - { - needsArrange(); - } -} - -// add new item at position -void LLMenuGL::insert( S32 position, LLView * ctrl, bool arrange /*= true*/ ) -{ - LLMenuItemGL * item = dynamic_cast(ctrl); - - if (NULL == item || position < 0 || position >= mItems.size()) - { - return; - } - - item_list_t::iterator position_iter = mItems.begin(); - std::advance(position_iter, position); - mItems.insert(position_iter, item); - LLUICtrl::addChild(item); - - if (arrange) - { - needsArrange(); - } -} - -// Adjust rectangle of the menu -void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom) -{ - setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom)); - needsArrange(); -} - -bool LLMenuGL::handleJumpKey(KEY key) -{ - // must perform case-insensitive comparison, so just switch to uppercase input key - key = toupper(key); - navigation_key_map_t::iterator found_it = mJumpKeys.find(key); - if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - // force highlight to close old menus and open and sub-menus - found_it->second->setHighlight(true); - found_it->second->onCommit(); - - } - // if we are navigating the menus, we need to eat the keystroke - // so rest of UI doesn't handle it - return true; -} - - -// Add the menu item to this menu. -bool LLMenuGL::append( LLMenuItemGL* item ) -{ - if (!item) return false; - mItems.push_back( item ); - LLUICtrl::addChild(item); - needsArrange(); - return true; -} - -// add a separator to this menu -bool LLMenuGL::addSeparator() -{ - LLMenuItemSeparatorGL::Params p; - LLMenuItemGL* separator = LLUICtrlFactory::create(p); - return addChild(separator); -} - -// add a menu - this will create a cascading menu -bool LLMenuGL::appendMenu( LLMenuGL* menu ) -{ - if( menu == this ) - { - LL_ERRS() << "** Attempt to attach menu to itself. This is certainly " - << "a logic error." << LL_ENDL; - } - bool success = true; - - LLMenuItemBranchGL::Params p; - p.name = menu->getName(); - p.label = menu->getLabel(); - p.branch = menu; - p.jump_key = menu->getJumpKey(); - p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - - LLMenuItemBranchGL* branch = LLUICtrlFactory::create(p); - success &= append( branch ); - - // Inherit colors - menu->setBackgroundColor( mBackgroundColor ); - menu->updateParent(LLMenuGL::sMenuContainer); - return success; -} - -// add a context menu branch -bool LLMenuGL::appendContextSubMenu(LLMenuGL *menu) -{ - if (menu == this) - { - LL_ERRS() << "Can't attach a context menu to itself" << LL_ENDL; - } - - LLContextMenuBranch *item; - LLContextMenuBranch::Params p; - p.name = menu->getName(); - p.label = menu->getLabel(); - p.branch = (LLContextMenu *)menu; - p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - - item = LLUICtrlFactory::create(p); - LLMenuGL::sMenuContainer->addChild(item->getBranch()); - - return append( item ); -} - -void LLMenuGL::setEnabledSubMenus(bool enable) -{ - setEnabled(enable); - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - (*item_iter)->setEnabledSubMenus( enable ); - } -} - -// setItemEnabled() - pass the label and the enable flag for a menu -// item. true will make sure it's enabled, false will disable it. -void LLMenuGL::setItemEnabled( const std::string& name, bool enable ) -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if( (*item_iter)->getName() == name ) - { - (*item_iter)->setEnabled( enable ); - (*item_iter)->setEnabledSubMenus( enable ); - break; - } - } -} - -void LLMenuGL::setItemVisible( const std::string& name, bool visible ) -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if( (*item_iter)->getName() == name ) - { - (*item_iter)->setVisible( visible ); - needsArrange(); - break; - } - } -} - - -void LLMenuGL::setItemLabel(const std::string &name, const std::string &label) -{ - LLMenuItemGL *item = getItem(name); - - if (item) - item->setLabel(label); -} - -void LLMenuGL::setItemLastSelected(LLMenuItemGL* item) -{ - if (getVisible()) - { - LLMenuHolderGL::setActivatedItem(item); - } - - // update enabled and checkmark status - item->buildDrawLabel(); -} - -// Set whether drop shadowed -void LLMenuGL::setDropShadowed( const bool shadowed ) -{ - mDropShadowed = shadowed; -} - -void LLMenuGL::setTornOff(bool torn_off) -{ - mTornOff = torn_off; -} - -U32 LLMenuGL::getItemCount() -{ - return mItems.size(); -} - -LLMenuItemGL* LLMenuGL::getItem(S32 number) -{ - if (number >= 0 && number < (S32)mItems.size()) - { - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if (number == 0) - { - return (*item_iter); - } - number--; - } - } - return NULL; -} - -LLMenuItemGL* LLMenuGL::getItem(std::string name) -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getName() == name) - { - return (*item_iter); - } - } - return NULL; -} - -LLMenuItemGL* LLMenuGL::getHighlightedItem() -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getHighlight()) - { - return (*item_iter); - } - } - return NULL; -} - -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()) - { - LLFloater * parent = dynamic_cast(getParent()); - if (parent) - { - parent->setFocus(true); - } - } - - // 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()) - { - next_item_iter = mItems.begin(); - } - else - { - 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(); - - // If current item is the last in the list, the menu is scrolled to the beginning - // and the first item is highlighted. - if (mScrollable && !scrollItems(SD_BEGIN)) - { - return NULL; - } - } - // If current item is the last visible, the menu is scrolled one item down - // and the next item is highlighted. - else if (mScrollable && - (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems) - { - // Call highlightNextItem() recursively only if the menu was successfully scrolled down. - // If scroll timer hasn't expired yet the menu won't be scrolled and calling - // highlightNextItem() will result in an endless recursion. - if (scrollItems(SD_DOWN)) - { - return highlightNextItem(cur_item, skip_disabled); - } - else - { - return NULL; - } - } - } - - // when first highlighting a menu, skip over tear off menu item - if (mTearOffItem && !cur_item) - { - // we know the first item is the tear off menu item - cur_item_iter = mItems.begin(); - next_item_iter++; - if (next_item_iter == mItems.end()) - { - next_item_iter = mItems.begin(); - } - } - - while(1) - { - // skip separators and disabled/invisible items - if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast(*next_item_iter)) - { - if (cur_item) - { - cur_item->setHighlight(false); - } - (*next_item_iter)->setHighlight(true); - return (*next_item_iter); - } - - - if (!skip_disabled || next_item_iter == cur_item_iter) - { - break; - } - - next_item_iter++; - if (next_item_iter == mItems.end()) - { - if (cur_item_iter == mItems.end()) - { - break; - } - next_item_iter = mItems.begin(); - } - } - - return NULL; -} - -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()) - { - LLFloater * parent = dynamic_cast(getParent()); - if (parent) - { - parent->setFocus(true); - } - } - - // 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()) - { - prev_item_iter = mItems.rbegin(); - } - else - { - 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(); - - // If current item is the first in the list, the menu is scrolled to the end - // and the last item is highlighted. - if (mScrollable && !scrollItems(SD_END)) - { - return NULL; - } - } - // If current item is the first visible, the menu is scrolled one item up - // and the previous item is highlighted. - else if (mScrollable && - std::distance(first_visible_item_iter, cur_item_iter) <= 0) - { - // Call highlightNextItem() only if the menu was successfully scrolled up. - // If scroll timer hasn't expired yet the menu won't be scrolled and calling - // highlightNextItem() will result in an endless recursion. - if (scrollItems(SD_UP)) - { - return highlightPrevItem(cur_item, skip_disabled); - } - else - { - return NULL; - } - } - } - - while(1) - { - // skip separators and disabled/invisible items - if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME) - { - (*prev_item_iter)->setHighlight(true); - return (*prev_item_iter); - } - - if (!skip_disabled || prev_item_iter == cur_item_iter) - { - break; - } - - prev_item_iter++; - if (prev_item_iter == mItems.rend()) - { - if (cur_item_iter == mItems.rend()) - { - break; - } - - prev_item_iter = mItems.rbegin(); - } - } - - return NULL; -} - -void LLMenuGL::buildDrawLabels() -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - (*item_iter)->buildDrawLabel(); - } -} - -void LLMenuGL::updateParent(LLView* parentp) -{ - if (getParent()) - { - getParent()->removeChild(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); - } -} - -bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const -{ - if (key == KEY_NONE) - { - return false; - } - // Note: checking this way because mAccelerators seems to be broken - // mAccelerators probably needs to be cleaned up or fixed - // It was used for dupplicate accelerator avoidance. - item_list_t::const_iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - LLMenuItemGL* itemp = *item_iter; - if (itemp->hasAccelerator(key, mask)) - { - return true; - } - } - return false; -} - -bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) -{ - // don't handle if not enabled - if(!getEnabled()) - { - return false; - } - - // Pass down even if not visible - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - LLMenuItemGL* itemp = *item_iter; - if (itemp->handleAcceleratorKey(key, mask)) - { - return true; - } - } - - return false; -} - -bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char ) -{ - if (jumpKeysActive()) - { - return handleJumpKey((KEY)uni_char); - } - return false; -} - -bool LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) -{ - // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU - bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; - S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; - S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; - LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y); - mouse_dir.normVec(); - LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY); - mouse_avg_dir.normVec(); - F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f)); - mMouseVelX = ll_round(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp)); - mMouseVelY = ll_round(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp)); - mLastMouseX = x; - mLastMouseY = y; - - // don't change menu focus unless mouse is moving or alt key is not held down - if ((llabs(mMouseVelX) > 0 || - llabs(mMouseVelY) > 0) && - (!mHasSelection || - //(mouse_delta_x == 0 && mouse_delta_y == 0) || - (mMouseVelX < 0) || - 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) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) - { - // moving mouse always highlights new item - if (mouse_delta_x != 0 || mouse_delta_y != 0) - { - ((LLMenuItemGL*)viewp)->setHighlight(false); - } - } - } - - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - //RN: always call handleHover to track mGotHover status - // but only set highlight when mouse is moving - if( viewp->getVisible() && - //RN: allow disabled items to be highlighted to preserve "active" menus when - // moving mouse through them - //viewp->getEnabled() && - viewp->pointInView(local_x, local_y) && - viewp->handleHover(local_x, local_y, mask)) - { - // moving mouse always highlights new item - if (mouse_delta_x != 0 || mouse_delta_y != 0) - { - ((LLMenuItemGL*)viewp)->setHighlight(true); - LLMenuGL::setKeyboardMode(false); - } - 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 ) -{ - if (!mScrollable) - return blockMouseEvent(x, y); - - if( clicks > 0 ) - { - while( clicks-- ) - scrollItems(SD_DOWN); - } - else - { - while( clicks++ ) - scrollItems(SD_UP); - } - - return true; -} - - -void LLMenuGL::draw( void ) -{ - if (mNeedsArrange) - { - arrange(); - mNeedsArrange = false; - } - if (mDropShadowed && !mTornOff) - { - static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow"); - gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, - color_drop_shadow, DROP_SHADOW_FLOATER); - } - - if( mBgVisible ) - { - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor.get() ); - } - LLView::draw(); -} - -void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha) -{ - LLColor4 color = itemp->getHighlightBgColor() % alpha; - gGL.color4fv( color.mV ); - LLRect item_rect = itemp->getRect(); - gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0); -} - -void LLMenuGL::setVisible(bool visible) -{ - if (visible != getVisible()) - { - if (!visible) - { - mFadeTimer.start(); - clearHoverItem(); - // reset last known mouse coordinates so - // we don't spoof a mouse move next time we're opened - mLastMouseX = 0; - mLastMouseY = 0; - } - else - { - mHasSelection = true; - mFadeTimer.stop(); - } - - LLView::setVisible(visible); - } -} - -LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, bool recurse) const -{ - LLView* view = findChildView(name, recurse); - if (view) - { - LLMenuItemBranchGL* branch = dynamic_cast(view); - if (branch) - { - return branch->getBranch(); - } - - LLMenuGL* menup = dynamic_cast(view); - if (menup) - { - return menup; - } - } - LL_WARNS() << "Child Menu " << name << " not found in menu " << getName() << LL_ENDL; - return NULL; -} - -bool LLMenuGL::clearHoverItem() -{ - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it; - if (itemp->getHighlight()) - { - itemp->setHighlight(false); - return true; - } - } - return false; -} - -void hide_top_view( LLView* view ) -{ - if( view ) view->setVisible( false ); -} - - -// 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, S32 mouse_x, S32 mouse_y) -{ - const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size - const S32 CURSOR_WIDTH = 12; - - if (menu->getChildList()->empty()) - { - return; - } - - menu->setVisible( true ); - - if(!menu->getAlwaysShowMenu()) - { - //Do not show menu if all menu items are disabled - bool item_enabled = false; - for (LLView::child_list_t::const_iterator itor = menu->getChildList()->begin(); - itor != menu->getChildList()->end(); - ++itor) - { - LLView *menu_item = (*itor); - item_enabled = item_enabled || menu_item->getEnabled(); - } - - if(!item_enabled) - { - menu->setVisible( false ); - return; - } - } - - // Resetting scrolling position - if (menu->isScrollable() && menu->isScrollPositionOnShowReset()) - { - menu->mFirstVisibleItem = NULL; - } - - // Fix menu rect if needed. - menu->needsArrange(); - menu->arrangeAndClear(); - - if ((mouse_x == 0) || (mouse_y == 0)) - - { - // 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() - - LLUI::getInstance()->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(); - S32 left = x + HPAD; - S32 top = y; - spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); - rect.setLeftTopAndSize( left, top, - rect.getWidth(), rect.getHeight() ); - menu->setRect( rect ); - - - // 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 ); - if (menu->getRect().mTop > menu_region_rect.mTop) - { - // not enough space: align with top, ignore exclusion - menu->translateIntoRect( menu_region_rect ); - } - menu->getParent()->sendChildToFront(menu); -} - -///============================================================================ -/// Class LLMenuBarGL -///============================================================================ - -static LLDefaultChildRegistry::Register r2("menu_bar"); - -LLMenuBarGL::LLMenuBarGL( const Params& p ) -: LLMenuGL(p), - mAltKeyTrigger(false) -{} - -// Default destructor -LLMenuBarGL::~LLMenuBarGL() -{ - std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer()); - mAccelerators.clear(); -} - -bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) -{ - if (getHighlightedItem() && mask == MASK_NONE) - { - // unmodified key accelerators are ignored when navigating menu - // (but are used as jump keys so will still work when appropriate menu is up) - return false; - } - bool result = LLMenuGL::handleAcceleratorKey(key, mask); - if (result && mask & MASK_ALT) - { - // ALT key used to trigger hotkey, don't use as shortcut to open menu - mAltKeyTrigger = false; - } - - if(!result - && (key == KEY_F10 && mask == MASK_CONTROL) - && !gKeyboard->getKeyRepeated(key) - && isInVisibleChain()) - { - if (getHighlightedItem()) - { - clearHoverItem(); - LLMenuGL::setKeyboardMode(false); - } - else - { - // close menus originating from other menu bars when first opening menu via keyboard - LLMenuGL::sMenuContainer->hideMenus(); - highlightNextItem(NULL); - LLMenuGL::setKeyboardMode(true); - } - return true; - } - - if (result && !getHighlightedItem() && LLMenuGL::sMenuContainer->hasVisibleMenu()) - { - // close menus originating from other menu bars - LLMenuGL::sMenuContainer->hideMenus(); - } - - return result; -} - -bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask) -{ - static LLUICachedControl use_altkey_for_menus ("UseAltKeyForMenus", 0); - if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus) - { - mAltKeyTrigger = true; - } - else // if any key other than ALT hit, clear out waiting for Alt key mode - { - mAltKeyTrigger = false; - } - - if (key == KEY_ESCAPE && mask == MASK_NONE) - { - LLMenuGL::setKeyboardMode(false); - // if any menus are visible, this will return true, stopping further processing of ESCAPE key - return LLMenuGL::sMenuContainer->hideMenus(); - } - - // before processing any other key, check to see if ALT key has triggered menu access - checkMenuTrigger(); - - return LLMenuGL::handleKeyHere(key, mask); -} - -bool LLMenuBarGL::handleJumpKey(KEY key) -{ - // perform case-insensitive comparison - key = toupper(key); - navigation_key_map_t::iterator found_it = mJumpKeys.find(key); - if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - found_it->second->setHighlight(true); - found_it->second->onCommit(); - } - return true; -} - -bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // 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()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - return LLMenuGL::handleMouseDown(x, y, mask); -} - -bool LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - return LLMenuGL::handleMouseDown(x, y, mask); -} - -void LLMenuBarGL::draw() -{ - LLMenuItemGL* itemp = getHighlightedItem(); - // If we are in mouse-control mode and the mouse cursor is not hovering over - // the current highlighted menu item and it isn't open, then remove the - // highlight. This is done via a polling mechanism here, as we don't receive - // notifications when the mouse cursor moves off of us - if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode()) - { - clearHoverItem(); - } - - checkMenuTrigger(); - - LLMenuGL::draw(); -} - - -void LLMenuBarGL::checkMenuTrigger() -{ - // has the ALT key been pressed and subsequently released? - if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT)) - { - // if alt key was released quickly, treat it as a menu access key - // otherwise it was probably an Alt-zoom or similar action - static LLUICachedControl menu_access_key_time ("MenuAccessKeyTime", 0); - if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= menu_access_key_time || - gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2) - { - if (getHighlightedItem()) - { - clearHoverItem(); - } - else - { - // close menus originating from other menu bars - LLMenuGL::sMenuContainer->hideMenus(); - - highlightNextItem(NULL); - LLMenuGL::setKeyboardMode(true); - } - } - mAltKeyTrigger = false; - } -} - -bool LLMenuBarGL::jumpKeysActive() -{ - // require user to be in keyboard navigation mode to activate key triggers - // as menu bars are always visible and it is easy to leave the mouse cursor over them - return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive(); -} - -// rearrange the child rects so they fit the shape of the menu bar. -void LLMenuBarGL::arrange( void ) -{ - U32 pos = 0; - LLRect rect( 0, getRect().getHeight(), 0, 0 ); - item_list_t::const_iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - LLMenuItemGL* item = *item_iter; - if (item->getVisible()) - { - rect.mLeft = pos; - pos += item->getNominalWidth(); - rect.mRight = pos; - item->setRect( rect ); - item->buildDrawLabel(); - } - } - reshape(rect.mRight, rect.getHeight()); -} - - -S32 LLMenuBarGL::getRightmostMenuEdge() -{ - // Find the last visible menu - item_list_t::reverse_iterator item_iter; - for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter) - { - if ((*item_iter)->getVisible()) - { - break; - } - } - - if (item_iter == mItems.rend()) - { - return 0; - } - return (*item_iter)->getRect().mRight; -} - -// add a vertical separator to this menu -bool LLMenuBarGL::addSeparator() -{ - LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL(); - return append( separator ); -} - -// add a menu - this will create a drop down menu. -bool LLMenuBarGL::appendMenu( LLMenuGL* menu ) -{ - if( menu == this ) - { - LL_ERRS() << "** Attempt to attach menu to itself. This is certainly " - << "a logic error." << LL_ENDL; - } - - bool success = true; - - // *TODO: Hack! Fix this - LLMenuItemBranchDownGL::Params p; - p.name = menu->getName(); - p.label = menu->getLabel(); - p.visible = menu->getVisible(); - p.branch = menu; - p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - p.font = menu->getFont(); - - LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create(p); - success &= branch->addToAcceleratorList(&mAccelerators); - success &= append( branch ); - branch->setJumpKey(branch->getJumpKey()); - menu->updateParent(LLMenuGL::sMenuContainer); - - return success; -} - -bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) -{ - bool handled = false; - LLView* active_menu = NULL; - - bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; - S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; - S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; - mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2); - mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2); - mLastMouseX = x; - mLastMouseY = y; - - // if nothing currently selected or mouse has moved since last call, pick menu item via mouse - // otherwise let keyboard control it - if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0) - { - // find current active menu - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if (((LLMenuItemGL*)viewp)->isOpen()) - { - active_menu = viewp; - } - } - - // check for new active menu - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->getVisible() && - viewp->getEnabled() && - viewp->pointInView(local_x, local_y) && - viewp->handleHover(local_x, local_y, mask)) - { - ((LLMenuItemGL*)viewp)->setHighlight(true); - handled = true; - if (active_menu && active_menu != viewp) - { - ((LLMenuItemGL*)viewp)->onCommit(); - LLMenuGL::setKeyboardMode(false); - } - LLMenuGL::setKeyboardMode(false); - } - } - - if (handled) - { - // set hover false on inactive menus - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) - { - ((LLMenuItemGL*)viewp)->setHighlight(false); - } - } - } - } - - getWindow()->setCursor(UI_CURSOR_ARROW); - - return true; -} - -///============================================================================ -/// Class LLMenuHolderGL -///============================================================================ -LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX); - -LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p) - : LLPanel(p) -{ - sItemActivationTimer.stop(); - mCanHide = true; -} - -void LLMenuHolderGL::draw() -{ - LLView::draw(); - // now draw last selected item as overlay - LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get(); - 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(); - - LLRect item_rect; - selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this); - - F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME; - - LLUI::pushMatrix(); - { - LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom); - selecteditem->getMenu()->drawBackground(selecteditem, interpolant); - selecteditem->draw(); - } - LLUI::popMatrix(); - } -} - -bool LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; - if (!handled) - { - LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu(); - LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL; - if (parent_menu && parent_menu->getVisible()) - { - // don't hide menu if parent was hit - LLRect parent_rect; - parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this); - if (!parent_rect.pointInRect(x, y)) - { - // clicked off of menu and parent, hide them all - hideMenus(); - } - } - else - { - // no visible parent, clicked off of menu, hide them all - hideMenus(); - } - } - return handled; -} - -bool LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL; - if (!handled) - { - // clicked off of menu, hide them all - hideMenus(); - } - 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 if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN)) - { - //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()) - { - hideMenus(); - } - LLView::reshape(width, height, called_from_parent); -} - -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) - { - return viewp; - } - } - return NULL; -} - - -bool LLMenuHolderGL::hideMenus() -{ - if (!mCanHide) - { - return false; - } - LLMenuGL::setKeyboardMode(false); - bool menu_visible = hasVisibleMenu(); - if (menu_visible) - { - // clicked off of menu, hide them all - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if (dynamic_cast(viewp) != NULL && viewp->getVisible()) - { - viewp->setVisible(false); - } - } - } - //if (gFocusMgr.childHasKeyboardFocus(this)) - //{ - // gFocusMgr.setKeyboardFocus(NULL); - //} - - return menu_visible; -} - -void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item) -{ - sItemLastSelectedHandle = item->getHandle(); - sItemActivationTimer.start(); -} - -///============================================================================ -/// Class LLTearOffMenu -///============================================================================ -LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : - LLFloater(LLSD()), - mQuitRequested(false) -{ - S32 floater_header_size = getHeaderHeight(); - - 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->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 = rect.getHeight() + floater_header_size; - reshape(rect.getWidth(), rect.getHeight()); - setRect(rect); - - // attach menu to floater - menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); - mOldParent = menup->getParent(); - addChild(menup); - menup->setVisible(true); - 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; - - // highlight first item (tear off item will be disabled) - mMenu->highlightNextItem(NULL); - - // Can't do this in postBuild() because that is only called for floaters - // constructed from XML. - mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this)); -} - -LLTearOffMenu::~LLTearOffMenu() -{ -} - -void LLTearOffMenu::draw() -{ - mMenu->setBackgroundVisible(isBackgroundOpaque()); - - if (getRect().getHeight() != mTargetHeight) - { - // animate towards target height - reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f)))); - } - mMenu->needsArrange(); - LLFloater::draw(); -} - -void LLTearOffMenu::onFocusReceived() -{ - if (mQuitRequested) - { - return; - } - - // if nothing is highlighted, just highlight first item - if (!mMenu->getHighlightedItem()) - { - mMenu->highlightNextItem(NULL); - } - - // parent menu items get highlights so navigation logic keeps working - LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem(); - while(parent_menu_item) - { - if (parent_menu_item->getMenu()->getVisible()) - { - parent_menu_item->setHighlight(true); - parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem(); - } - else - { - break; - } - } - LLFloater::onFocusReceived(); -} - -void LLTearOffMenu::onFocusLost() -{ - // remove highlight from parent item and our own menu - mMenu->clearHoverItem(); - LLFloater::onFocusLost(); -} - -bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent) -{ - // pass keystrokes down to menu - return mMenu->handleUnicodeChar(uni_char, true); -} - -bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask) -{ - if (!mMenu->getHighlightedItem()) - { - if (key == KEY_UP) - { - mMenu->highlightPrevItem(NULL); - return true; - } - else if (key == KEY_DOWN) - { - mMenu->highlightNextItem(NULL); - return true; - } - } - // pass keystrokes down to menu - return mMenu->handleKey(key, mask, true); -} - -void LLTearOffMenu::translate(S32 x, S32 y) -{ - if (x != 0 && y != 0) - { - // hide open sub-menus by clearing current hover item - mMenu->clearHoverItem(); - } - LLFloater::translate(x, y); -} - -//static -LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup) -{ - LLTearOffMenu* tearoffp = new LLTearOffMenu(menup); - // keep onscreen - gFloaterView->adjustToFitScreen(tearoffp, false); - tearoffp->openFloater(LLSD()); - - return tearoffp; -} - -void LLTearOffMenu::updateSize() -{ - if (mMenu) - { - S32 floater_header_size = getHeaderHeight(); - const LLRect &floater_rect = getRect(); - LLRect new_rect; - mMenu->localRectToOtherView(LLRect(-1, mMenu->getRect().getHeight() + floater_header_size, mMenu->getRect().getWidth() + 3, 0), &new_rect, gFloaterView); - - if (floater_rect.getWidth() != new_rect.getWidth() - || mTargetHeight != new_rect.getHeight()) - { - // make sure this floater is big enough for menu - mTargetHeight = new_rect.getHeight(); - reshape(new_rect.getWidth(), mTargetHeight); - - // Restore menu position - LLRect menu_rect = mMenu->getRect(); - menu_rect.setOriginAndSize(1, 1, - menu_rect.getWidth(), menu_rect.getHeight()); - mMenu->setRect(menu_rect); - } - } -} - -void LLTearOffMenu::closeTearOff() -{ - removeChild(mMenu); - mOldParent->addChild(mMenu); - mMenu->clearHoverItem(); - mMenu->setFollowsNone(); - mMenu->setBackgroundVisible(true); - mMenu->setVisible(false); - mMenu->setTornOff(false); - mMenu->setDropShadowed(true); - mQuitRequested = true; -} - -LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p) -: LLMenuItemGL(p) -{ - LLContextMenu* branch = static_cast(p.branch); - if (branch) - { - mBranch = branch->getHandle(); - branch->hide(); - branch->setParentMenuItem(this); - } -} - -LLContextMenuBranch::~LLContextMenuBranch() -{ - if (mBranch.get()) - { - mBranch.get()->die(); - } -} - -// called to rebuild the draw label -void LLContextMenuBranch::buildDrawLabel( void ) -{ - auto menu = getBranch(); - if (menu) - { - // default enablement is this -- if any of the subitems are - // enabled, this item is enabled. JC - U32 sub_count = menu->getItemCount(); - U32 i; - bool any_enabled = false; - for (i = 0; i < sub_count; i++) - { - LLMenuItemGL* item = menu->getItem(i); - item->buildDrawLabel(); - if (item->getEnabled() && !item->getDrawTextDisabled() ) - { - any_enabled = true; - break; - } - } - setDrawTextDisabled(!any_enabled); - setEnabled(true); - } - - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel; - appendAcceleratorString( st ); - mDrawAccelLabel = st; - - mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; -} - -void LLContextMenuBranch::showSubMenu() -{ - auto menu = getBranch(); - if(menu) - { - LLMenuItemGL* menu_item = menu->getParentMenuItem(); - if (menu_item != NULL && menu_item->getVisible()) - { - S32 center_x; - S32 center_y; - localPointToScreen(getRect().getWidth(), getRect().getHeight(), ¢er_x, ¢er_y); - menu->show(center_x, center_y); - } - } -} - -// onCommit() - do the primary funcationality of the menu item. -void LLContextMenuBranch::onCommit( void ) -{ - showSubMenu(); - -} -void LLContextMenuBranch::setHighlight( bool highlight ) -{ - if (highlight == getHighlight()) return; - LLMenuItemGL::setHighlight(highlight); - auto menu = getBranch(); - if (menu) - { - if (highlight) - { - showSubMenu(); - } - else - { - menu->hide(); - } - } -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -//----------------------------------------------------------------------------- -// class LLContextMenu -// A context menu -//----------------------------------------------------------------------------- -static LLDefaultChildRegistry::Register context_menu_register("context_menu"); -static MenuRegistry::Register context_menu_register2("context_menu"); - - -LLContextMenu::LLContextMenu(const Params& p) -: LLMenuGL(p), - mHoveredAnyItem(false), - mHoverItem(NULL) -{ - //setBackgroundVisible(true); -} - -void LLContextMenu::setVisible(bool visible) -{ - if (!visible) - hide(); -} - -// Takes cursor position in screen space? -void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view) -{ - if (getChildList()->empty()) - { - // nothing to show, so abort - 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 LLMenuGL::showPopup() - LLMenuHolderGL::sContextMenuSpawnPos.set(x,y); - - arrangeAndClear(); - - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); - LLView* parent_view = getParent(); - - // Open upwards if menu extends past bottom - if (y - height < menu_region_rect.mBottom) - { - if (getParentMenuItem()) // Adjust if this is a submenu - { - y += height - getParentMenuItem()->getNominalHeight(); - } - else - { - y += height; - } - } - - // Open out to the left if menu extends past right edge - if (x + width > menu_region_rect.mRight) - { - if (getParentMenuItem()) - { - x -= getParentMenuItem()->getRect().getWidth() + width; - } - else - { - x -= width; - } - } - - S32 local_x, local_y; - parent_view->screenPointToLocal(x, y, &local_x, &local_y); - - LLRect rect; - rect.setLeftTopAndSize(local_x, local_y, width, height); - setRect(rect); - arrange(); - - if (spawning_view) - { - mSpawningViewHandle = spawning_view->getHandle(); - } - else - { - mSpawningViewHandle.markDead(); - } - LLView::setVisible(true); -} - -void LLContextMenu::hide() -{ - if (!getVisible()) return; - - LLView::setVisible(false); - - if (mHoverItem) - { - mHoverItem->setHighlight( false ); - } - mHoverItem = NULL; -} - - -bool LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) -{ - LLMenuGL::handleHover(x,y,mask); - - bool handled = false; - - LLMenuItemGL *item = getHighlightedItem(); - - if (item && item->getEnabled()) - { - getWindow()->setCursor(UI_CURSOR_ARROW); - handled = true; - - if (item != mHoverItem) - { - if (mHoverItem) - { - mHoverItem->setHighlight( false ); - } - mHoverItem = item; - mHoverItem->setHighlight( true ); - } - mHoveredAnyItem = true; - } - else - { - // clear out our selection - if (mHoverItem) - { - mHoverItem->setHighlight(false); - mHoverItem = NULL; - } - } - - if( !handled && pointInView( x, y ) ) - { - getWindow()->setCursor(UI_CURSOR_ARROW); - handled = true; - } - - return handled; -} - -// handleMouseDown and handleMouseUp are handled by LLMenuGL - - -bool LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // The click was somewhere within our rectangle - LLMenuItemGL *item = getHighlightedItem(); - - S32 local_x = x - getRect().mLeft; - S32 local_y = y - getRect().mBottom; - - bool clicked_in_menu = pointInView(local_x, local_y) ; - - // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie - if (clicked_in_menu) - { - // capture mouse cursor as if on initial menu show - handled = true; - } - - if (item) - { - // lie to the item about where the click happened - // to make sure it's within the item's rectangle - if (item->handleMouseDown( 0, 0, mask )) - { - handled = true; - } - } - - return handled; -} - -bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) -{ - S32 local_x = x - getRect().mLeft; - S32 local_y = y - getRect().mBottom; - - if (!mHoveredAnyItem && !pointInView(local_x, local_y)) - { - sMenuContainer->hideMenus(); - return true; - } - - - bool result = handleMouseUp( x, y, mask ); - mHoveredAnyItem = false; - - return result; -} - -bool LLContextMenu::addChild(LLView* view, S32 tab_group) -{ - return addContextChild(view, tab_group); -} - +/** + * @file llmenugl.cpp + * @brief LLMenuItemGL base class + * + * $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$ + */ + +//***************************************************************************** +// +// This file contains the opengl based menu implementation. +// +// NOTES: A menu label is split into 4 columns. The left column, the +// label colum, the accelerator column, and the right column. The left +// column is used for displaying boolean values for toggle and check +// controls. The right column is used for submenus. +// +//***************************************************************************** + +//#include "llviewerprecompiledheaders.h" +#include "linden_common.h" + +#include "llmenugl.h" + +#include "llgl.h" +#include "llmath.h" +#include "llrender.h" +#include "llfocusmgr.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 "llstl.h" + +#include "v2math.h" +#include +#include + +// static +LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL; +view_listener_t::listener_map_t view_listener_t::sListeners; + +S32 MENU_BAR_HEIGHT = 18; +S32 MENU_BAR_WIDTH = 410; + +///============================================================================ +/// Local function declarations, constants, enums, and typedefs +///============================================================================ + +const S32 LABEL_BOTTOM_PAD_PIXELS = 2; + +const U32 LEFT_PAD_PIXELS = 3; +const U32 LEFT_WIDTH_PIXELS = 15; +const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS; + +const U32 RIGHT_PAD_PIXELS = 7; +const U32 RIGHT_WIDTH_PIXELS = 15; +const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; + +const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; + +const U32 BRIEF_PAD_PIXELS = 2; + +const U32 SEPARATOR_HEIGHT_PIXELS = 8; +const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10; +const S32 MENU_ITEM_PADDING = 4; + +const std::string SEPARATOR_NAME("separator"); +const std::string VERTICAL_SEPARATOR_LABEL( "|" ); + +const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK +const std::string LLMenuGL::BRANCH_SUFFIX( "\xe2\x96\xb8" ); // 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; + +bool LLMenuGL::sKeyboardMode = false; + +LLHandle LLMenuHolderGL::sItemLastSelectedHandle; +LLFrameTimer LLMenuHolderGL::sItemActivationTimer; + +const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f; + +static MenuRegistry::Register register_menu_item("menu_item"); +static MenuRegistry::Register register_separator("menu_item_separator"); +static MenuRegistry::Register register_menu_item_call("menu_item_call"); +static MenuRegistry::Register register_menu_item_check("menu_item_check"); +// Created programmatically but we need to specify custom colors in xml +static MenuRegistry::Register register_menu_item_tear_off("menu_item_tear_off"); +static MenuRegistry::Register register_menu("menu"); + +static LLDefaultChildRegistry::Register register_menu_default("menu"); + + + +///============================================================================ +/// Class LLMenuItemGL +///============================================================================ + +LLMenuItemGL::Params::Params() +: shortcut("shortcut"), + jump_key("jump_key", KEY_NONE), + use_mac_ctrl("use_mac_ctrl", false), + allow_key_repeat("allow_key_repeat", false), + rect("rect"), + left("left"), + top("top"), + right("right"), + bottom("bottom"), + width("width"), + height("height"), + bottom_delta("bottom_delta"), + left_delta("left_delta"), + enabled_color("enabled_color"), + disabled_color("disabled_color"), + highlight_bg_color("highlight_bg_color"), + highlight_fg_color("highlight_fg_color") +{ + changeDefault(mouse_opaque, true); +} + +// Default constructor +LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) +: LLUICtrl(p), + mJumpKey(p.jump_key), + mAllowKeyRepeat(p.allow_key_repeat), + mHighlight( false ), + mGotHover( false ), + mBriefItem( false ), + mDrawTextDisabled( false ), + mFont(p.font), + mAcceleratorKey(KEY_NONE), + mAcceleratorMask(MASK_NONE), + mLabel(p.label.isProvided() ? p.label() : p.name()), + mEnabledColor(p.enabled_color()), + mDisabledColor(p.disabled_color()), + mHighlightBackground(p.highlight_bg_color()), + mHighlightForeground(p.highlight_fg_color()) +{ +#ifdef LL_DARWIN + // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd + bool useMacCtrl = p.use_mac_ctrl; +#endif // LL_DARWIN + + std::string shortcut = p.shortcut; + if (shortcut.find("control") != shortcut.npos) + { +#ifdef LL_DARWIN + if ( useMacCtrl ) + { + mAcceleratorMask |= MASK_MAC_CONTROL; + } +#endif // LL_DARWIN + mAcceleratorMask |= MASK_CONTROL; + } + if (shortcut.find("alt") != shortcut.npos) + { + mAcceleratorMask |= MASK_ALT; + } + if (shortcut.find("shift") != shortcut.npos) + { + mAcceleratorMask |= MASK_SHIFT; + } + S32 pipe_pos = shortcut.rfind("|"); + std::string key_str = shortcut.substr(pipe_pos+1); + + LLKeyboard::keyFromString(key_str, &mAcceleratorKey); + + LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut + << ", key str: " << key_str + << ", accelerator mask: " << mAcceleratorMask + << ", accelerator key: " << mAcceleratorKey + << LL_ENDL; +} + +//virtual +void LLMenuItemGL::setValue(const LLSD& value) +{ + setLabel(value.asString()); +} + +//virtual +LLSD LLMenuItemGL::getValue() const +{ + return getLabel(); +} + +//virtual +bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + return (mAcceleratorKey == key) && (mAcceleratorMask == mask); +} + +//virtual +bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) +{ + if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) + { + onCommit(); + return true; + } + return false; +} + +bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) +{ + getWindow()->setCursor(UI_CURSOR_ARROW); + return true; +} + +//virtual +bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + return LLUICtrl::handleRightMouseDown(x,y,mask); +} + +void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask) +{ + setHover(true); + LLUICtrl::onMouseEnter(x,y,mask); +} + +void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask) +{ + setHover(false); + LLUICtrl::onMouseLeave(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) +{ + LLMenuKeyboardBinding *accelerator = NULL; + + if (mAcceleratorKey != KEY_NONE) + { + std::list::iterator list_it; + for (list_it = listp->begin(); list_it != listp->end(); ++list_it) + { + accelerator = *list_it; + if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS))) + { + + // *NOTE: get calling code to throw up warning or route + // warning messages back to app-provided output + // std::string warning; + // warning.append("Duplicate key binding <"); + // appendAcceleratorString( warning ); + // warning.append("> for menu items:\n "); + // warning.append(accelerator->mName); + // warning.append("\n "); + // warning.append(mLabel); + + // LL_WARNS() << warning << LL_ENDL; + // LLAlertDialog::modalAlert(warning); + return false; + } + } + if (!accelerator) + { + accelerator = new LLMenuKeyboardBinding; + if (accelerator) + { + accelerator->mKey = mAcceleratorKey; + accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS); +// accelerator->mName = mLabel; + } + listp->push_back(accelerator);//addData(accelerator); + } + } + return true; +} + +// This function appends the character string representation of +// the current accelerator key and mask to the provided string. +void LLMenuItemGL::appendAcceleratorString( std::string& st ) const +{ + st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey ); + LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL; +} + +void LLMenuItemGL::setJumpKey(KEY key) +{ + mJumpKey = LLStringOps::toUpper((char)key); +} + + +// virtual +U32 LLMenuItemGL::getNominalHeight( void ) const +{ + return 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() const +{ + return (LLMenuGL*) getParent(); +} + + +// getNominalWidth() - returns the normal width of this control in +// pixels - this is used for calculating the widest item, as well as +// for horizontal arrangement. +U32 LLMenuItemGL::getNominalWidth( void ) const +{ + U32 width; + + if (mBriefItem) + { + width = BRIEF_PAD_PIXELS; + } + else + { + width = PLAIN_PAD_PIXELS; + } + + if( KEY_NONE != mAcceleratorKey ) + { + width += getMenu()->getShortcutPad(); + std::string temp; + appendAcceleratorString( temp ); + width += mFont->getWidth( temp ); + } + width += mFont->getWidth( mLabel.getWString().c_str() ); + return width; +} + +// called to rebuild the draw label +void LLMenuItemGL::buildDrawLabel( void ) +{ + mDrawAccelLabel.clear(); + std::string st = mDrawAccelLabel.getString(); + appendAcceleratorString( st ); + mDrawAccelLabel = st; +} + +void LLMenuItemGL::onCommit( void ) +{ + // 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()) + { + LLMenuGL::sMenuContainer->hideMenus(); + } + + LLUICtrl::onCommit(); +} + +// set the hover status (called by it's menu) + void LLMenuItemGL::setHighlight( bool highlight ) +{ + if (highlight) + { + getMenu()->clearHoverItem(); + } + + if (mHighlight != highlight) + { + dirtyRect(); + } + + mHighlight = highlight; +} + + +bool LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) +{ + if (getHighlight() && + getMenu()->isOpen()) + { + if (key == KEY_UP) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + getMenu()->highlightPrevItem(this); + return true; + } + else if (key == KEY_DOWN) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + getMenu()->highlightNextItem(this); + return true; + } + else if (key == KEY_RETURN && mask == MASK_NONE) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + onCommit(); + return true; + } + } + + return false; +} + +bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask) +{ + // switch to mouse navigation mode + LLMenuGL::setKeyboardMode(false); + + onCommit(); + make_ui_sound("UISndClickRelease"); + return LLView::handleMouseUp(x, y, mask); +} + +bool LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask) +{ + // switch to mouse navigation mode + LLMenuGL::setKeyboardMode(false); + + setHighlight(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 !getMenu()->isScrollable(); +} + +void LLMenuItemGL::draw( void ) +{ + // *FIX: This can be optimized by using switches. Want to avoid + // that until the functionality is finalized. + + // HACK: Brief items don't highlight. Pie menu takes care of it. JC + // let disabled items be highlighted, just don't draw them as such + if( getEnabled() && getHighlight() && !mBriefItem) + { + gGL.color4fv( mHighlightBackground.get().mV ); + + gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); + } + + LLColor4 color; + + if ( getEnabled() && getHighlight() ) + { + color = mHighlightForeground.get(); + } + else if( getEnabled() && !mDrawTextDisabled ) + { + color = mEnabledColor.get(); + } + else + { + color = mDisabledColor.get(); + } + + // Highlight if needed + if( ll::ui::SearchableControl::getHighlighted() ) + color = ll::ui::SearchableControl::getHighlightColor(); + + // Draw the text on top. + if (mBriefItem) + { + mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL); + } + else + { + if( !mDrawBoolLabel.empty() ) + { + mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); + } + mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); + if( !mDrawAccelLabel.empty() ) + { + mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, + LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); + } + if( !mDrawBranchLabel.empty() ) + { + mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, + LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); + } + } + + // underline "jump" key only when keyboard navigation has been initiated + if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) + { + std::string upper_case_label = mLabel.getString(); + LLStringUtil::toUpper(upper_case_label); + std::string::size_type offset = upper_case_label.find(mJumpKey); + if (offset != std::string::npos) + { + S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset); + S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1); + gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1); + } + } +} + +bool LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + mLabel.setArg(key, text); + return true; +} + +void LLMenuItemGL::onVisibilityChange(bool new_visibility) +{ + if (getMenu()) + { + getMenu()->needsArrange(); + } + LLView::onVisibilityChange(new_visibility); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemSeparatorGL +// +// This class represents a separator. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LLMenuItemSeparatorGL::Params::Params() + : on_visible("on_visible") +{ +} + +LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) : + LLMenuItemGL( p ) +{ + if (p.on_visible.isProvided()) + { + mVisibleSignal.connect(initEnableCallback(p.on_visible)); + } +} + +//virtual +U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const +{ + return SEPARATOR_HEIGHT_PIXELS; +} + +void LLMenuItemSeparatorGL::draw( void ) +{ + gGL.color4fv( mDisabledColor.get().mV ); + const S32 y = getRect().getHeight() / 2; + const S32 PAD = 6; + gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); +} + +void LLMenuItemSeparatorGL::buildDrawLabel( void ) +{ + if (mVisibleSignal.num_slots() > 0) + { + bool visible = mVisibleSignal(this, LLSD()); + setVisible(visible); + } +} + +bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) +{ + LLMenuGL* parent_menu = getMenu(); + if (y > getRect().getHeight() / 2) + { + // 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->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : false; + } + else + { + LLView* next_menu_item = parent_menu->findPrevSibling(this); + return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : false; + } +} + +bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) +{ + LLMenuGL* parent_menu = getMenu(); + if (y > getRect().getHeight() / 2) + { + LLView* prev_menu_item = parent_menu->findNextSibling(this); + return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : false; + } + else + { + LLView* next_menu_item = parent_menu->findPrevSibling(this); + return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : false; + } +} + +bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) +{ + LLMenuGL* parent_menu = getMenu(); + if (y > getRect().getHeight() / 2) + { + parent_menu->highlightPrevItem(this, false); + return false; + } + else + { + parent_menu->highlightNextItem(this, false); + return false; + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemVerticalSeparatorGL +// +// This class represents a vertical separator. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuItemVerticalSeparatorGL +: public LLMenuItemSeparatorGL +{ +public: + LLMenuItemVerticalSeparatorGL( void ); + + virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; } +}; + +LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) +{ + setLabel( VERTICAL_SEPARATOR_LABEL ); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemTearOffGL +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p) +: LLMenuItemGL(p) +{ +} + +// 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::onCommit() +{ + if (getMenu()->getTornOff()) + { + LLTearOffMenu * torn_off_menu = dynamic_cast(getMenu()->getParent()); + if (torn_off_menu) + { + torn_off_menu->closeFloater(); + } + } + else + { + // transfer keyboard focus and highlight to first real item in list + if (getHighlight()) + { + getMenu()->highlightNextItem(this); + } + + getMenu()->needsArrange(); + + LLFloater* parent_floater = getParentFloater(); + LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu()); + + if (tear_off_menu) + { + if (parent_floater) + { + parent_floater->addDependentFloater(tear_off_menu, false); + } + + // give focus to torn off menu because it will have + // been taken away when parent menu closes + tear_off_menu->setFocus(true); + } + } + LLMenuItemGL::onCommit(); +} + +void LLMenuItemTearOffGL::draw() +{ + // disabled items can be highlighted, but shouldn't render as such + if( getEnabled() && getHighlight() && !isBriefItem()) + { + gGL.color4fv( mHighlightBackground.get().mV ); + gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); + } + + if (getEnabled()) + { + gGL.color4fv( mEnabledColor.get().mV ); + } + else + { + gGL.color4fv( mDisabledColor.get().mV ); + } + const S32 y = getRect().getHeight() / 3; + const S32 PAD = 6; + gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); + gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 ); +} + +U32 LLMenuItemTearOffGL::getNominalHeight( void ) const +{ + return TEAROFF_SEPARATOR_HEIGHT_PIXELS; +} + +///============================================================================ +/// Class LLMenuItemCallGL +///============================================================================ + +LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p) +: LLMenuItemGL(p) +{ +} + +void LLMenuItemCallGL::initFromParams(const Params& p) +{ + if (p.on_visible.isProvided()) + { + mVisibleSignal.connect(initEnableCallback(p.on_visible)); + } + if (p.on_enable.isProvided()) + { + setEnableCallback(initEnableCallback(p.on_enable)); + // Set the enabled control variable (for backwards compatability) + if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty()) + { + LLControlVariable* control = findControl(p.on_enable.control_name()); + if (control) + { + setEnabledControlVariable(control); + } + else + { + LL_WARNS() << "Failed to assign 'enabled' control variable to menu " << getName() + << ": control " << p.on_enable.control_name() + << " does not exist." << LL_ENDL; + } + } + } + if (p.on_click.isProvided()) + { + setCommitCallback(initCommitCallback(p.on_click)); + } + + LLUICtrl::initFromParams(p); +} + +void LLMenuItemCallGL::onCommit( void ) +{ + // RN: menu item can be deleted in callback, so beware + getMenu()->setItemLastSelected( this ); + + LLMenuItemGL::onCommit(); +} + +void LLMenuItemCallGL::updateEnabled( void ) +{ + if (mEnableSignal.num_slots() > 0) + { + bool enabled = mEnableSignal(this, LLSD()); + if (mEnabledControlVariable) + { + if (!enabled) + { + // callback overrides control variable; this will call setEnabled() + mEnabledControlVariable->set(false); + } + } + else + { + setEnabled(enabled); + } + } +} + +void LLMenuItemCallGL::updateVisible( void ) +{ + if (mVisibleSignal.num_slots() > 0) + { + bool visible = mVisibleSignal(this, LLSD()); + setVisible(visible); + } +} + +void LLMenuItemCallGL::buildDrawLabel( void ) +{ + updateEnabled(); + updateVisible(); + 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)) ) + { + updateEnabled(); + if (getEnabled()) + { + onCommit(); + return true; + } + } + return false; +} + +// handleRightMouseUp moved into base class LLMenuItemGL so clicks are +// handled for all menu item types + +///============================================================================ +/// Class LLMenuItemCheckGL +///============================================================================ +LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p) +: LLMenuItemCallGL(p) +{ +} + +void LLMenuItemCheckGL::initFromParams(const Params& p) +{ + if (p.on_check.isProvided()) + { + setCheckCallback(initEnableCallback(p.on_check)); + // Set the control name (for backwards compatability) + if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty()) + { + setControlName(p.on_check.control_name()); + } + } + + LLMenuItemCallGL::initFromParams(p); +} + +void LLMenuItemCheckGL::onCommit( void ) +{ + LLMenuItemCallGL::onCommit(); +} + +//virtual +void LLMenuItemCheckGL::setValue(const LLSD& value) +{ + LLUICtrl::setValue(value); + if(value.asBoolean()) + { + mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; + } + else + { + mDrawBoolLabel.clear(); + } +} + +//virtual +LLSD LLMenuItemCheckGL::getValue() const +{ + // Get our boolean value from the view model. + // If we don't override this method then the implementation from + // LLMenuItemGL will return a string. (EXT-8501) + return LLUICtrl::getValue(); +} + +// called to rebuild the draw label +void LLMenuItemCheckGL::buildDrawLabel( void ) +{ + // Note: mCheckSignal() returns true if no callbacks are set + bool checked = mCheckSignal(this, LLSD()); + if (mControlVariable) + { + if (!checked) + setControlValue(false); // callback overrides control variable; this will call setValue() + } + else + { + setValue(checked); + } + if(getValue().asBoolean()) + { + mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; + } + else + { + mDrawBoolLabel.clear(); + } + LLMenuItemCallGL::buildDrawLabel(); +} + +///============================================================================ +/// Class LLMenuItemBranchGL +///============================================================================ +LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p) + : LLMenuItemGL(p) +{ + LLMenuGL* branch = p.branch; + if (branch) + { + mBranchHandle = branch->getHandle(); + branch->setVisible(false); + branch->setParentMenuItem(this); + } +} + +LLMenuItemBranchGL::~LLMenuItemBranchGL() +{ + if (mBranchHandle.get()) + { + mBranchHandle.get()->die(); + } +} + + + +// virtual +LLView* LLMenuItemBranchGL::getChildView(const std::string& name, bool recurse) const +{ + LLMenuGL* branch = getBranch(); + if (branch) + { + if (branch->getName() == name) + { + return branch; + } + + // Always recurse on branches + return branch->getChildView(name, recurse); + } + + return LLView::getChildView(name, recurse); +} + +LLView* LLMenuItemBranchGL::findChildView(const std::string& name, bool recurse) const +{ + LLMenuGL* branch = getBranch(); + if (branch) + { + if (branch->getName() == name) + { + return branch; + } + + // Always recurse on branches + return branch->findChildView(name, recurse); + } + + return LLView::findChildView(name, recurse); +} + +// virtual +bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) +{ + // switch to mouse navigation mode + LLMenuGL::setKeyboardMode(false); + + onCommit(); + make_ui_sound("UISndClickRelease"); + return true; +} + +bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + return getBranch() && getBranch()->hasAccelerator(key, mask); +} + +bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) +{ + return getBranch() && getBranch()->handleAcceleratorKey(key, mask); +} + +// This function checks to see if the accelerator key is already in use; +// if not, it will be added to the list +bool LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) +{ + LLMenuGL* branch = getBranch(); + if (!branch) + return false; + + U32 item_count = branch->getItemCount(); + LLMenuItemGL *item; + + while (item_count--) + { + if ((item = branch->getItem(item_count))) + { + return item->addToAcceleratorList(listp); + } + } + + return false; +} + + +// called to rebuild the draw label +void LLMenuItemBranchGL::buildDrawLabel( void ) +{ + mDrawAccelLabel.clear(); + std::string st = mDrawAccelLabel; + appendAcceleratorString( st ); + mDrawAccelLabel = st; + mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; +} + +void LLMenuItemBranchGL::onCommit( void ) +{ + openMenu(); + + // keyboard navigation automatically propagates highlight to sub-menu + // to facilitate fast menu control via jump keys + if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem()) + { + getBranch()->highlightNextItem(NULL); + } + + LLUICtrl::onCommit(); +} + +bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent) +{ + bool handled = false; + if (getBranch() && called_from_parent) + { + handled = getBranch()->handleKey(key, mask, called_from_parent); + } + + if (!handled) + { + handled = LLMenuItemGL::handleKey(key, mask, called_from_parent); + } + + return handled; +} + +bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, bool called_from_parent) +{ + bool handled = false; + if (getBranch() && called_from_parent) + { + handled = getBranch()->handleUnicodeChar(uni_char, true); + } + + if (!handled) + { + handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent); + } + + return handled; +} + + +void LLMenuItemBranchGL::setHighlight( bool highlight ) +{ + if (highlight == getHighlight()) + return; + + 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 + LLFloater * menu_parent = dynamic_cast(getMenu()->getParent()); + if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus()) + { + auto_open = false; + } + // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus) + if (branch->getTornOff()) + { + auto_open = false; + } + LLMenuItemGL::setHighlight(highlight); + if( highlight ) + { + if(auto_open) + { + openMenu(); + } + } + else + { + if (branch->getTornOff()) + { + LLFloater * branch_parent = dynamic_cast(branch->getParent()); + if (branch_parent) + { + branch_parent->setFocus(false); + } + branch->clearHoverItem(); + } + else + { + branch->setVisible( false ); + } + } +} + +void LLMenuItemBranchGL::draw() +{ + LLMenuItemGL::draw(); + if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff()) + { + setHighlight(true); + } +} + +void LLMenuItemBranchGL::updateBranchParent(LLView* parentp) +{ + if (getBranch() && getBranch()->getParent() == NULL) + { + // make the branch menu a sibling of my parent menu + getBranch()->updateParent(parentp); + } +} + +void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility) +{ + if (!new_visibility && getBranch() && !getBranch()->getTornOff()) + { + getBranch()->setVisible(false); + } + LLMenuItemGL::onVisibilityChange(new_visibility); +} + +bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask) +{ + 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())) + { + if (branch->getVisible() && key == KEY_LEFT) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + bool handled = branch->clearHoverItem(); + if (branch->getTornOff()) + { + LLFloater * branch_parent = dynamic_cast(branch->getParent()); + if (branch_parent) + { + branch_parent->setFocus(false); + } + } + if (handled && getMenu()->getTornOff()) + { + LLFloater * menu_parent = dynamic_cast(getMenu()->getParent()); + if (menu_parent) + { + menu_parent->setFocus(true); + } + } + return handled; + } + + if (key == KEY_RIGHT && !branch->getHighlightedItem()) + { + // 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() +{ + LLMenuGL* branch = getBranch(); + if (!branch) + return; + + if (branch->getTornOff()) + { + LLFloater * branch_parent = dynamic_cast(branch->getParent()); + if (branch_parent) + { + gFloaterView->bringToFront(branch_parent); + // this might not be necessary, as torn off branches don't get focus and hence no highligth + branch->highlightNextItem(NULL); + } + } + else if( !branch->getVisible() ) + { + // get valid rectangle for menus + const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); + + branch->arrange(); + + 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, branch->getParent()); + + branch_rect.setLeftTopAndSize( left, top, + branch_rect.getWidth(), branch_rect.getHeight() ); + + if (branch->getCanTearOff()) + { + branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); + } + 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; + branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); + if( y < menu_region_rect.mBottom ) + { + // open upwards if menu extends past bottom + // adjust by the height of the menu item branch since it is a submenu + if (y + 2 * branch_rect.getHeight() - getRect().getHeight() > menu_region_rect.mTop) + { + // overlaps with top border, align with top + delta_y = menu_region_rect.mTop - y - branch_rect.getHeight(); + } + else + { + delta_y = branch_rect.getHeight() - getRect().getHeight(); + } + } + + if( x + branch_rect.getWidth() > menu_region_rect.mRight ) + { + // move sub-menu over to left side + delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth()))); + } + branch->translate( delta_x, delta_y ); + + branch->setVisible( true ); + branch->getParent()->sendChildToFront(branch); + + dirtyRect(); + } +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemBranchDownGL +// +// The LLMenuItemBranchDownGL represents a menu item that has a +// sub-menu. This is used to make menu bar menus. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuItemBranchDownGL : public LLMenuItemBranchGL +{ +protected: + +public: + LLMenuItemBranchDownGL( const Params& ); + + // returns the normal width of this control in pixels - this is + // used for calculating the widest item, as well as for horizontal + // arrangement. + virtual U32 getNominalWidth( void ) const; + + // called to rebuild the draw label + virtual void buildDrawLabel( void ); + + // handles opening, positioning, and arranging the menu branch associated with this item + virtual void openMenu( void ); + + // set the hover status (called by it's menu) and if the object is + // active. This is used for behavior transfer. + virtual void setHighlight( bool highlight ); + + virtual bool isActive( void ) const; + + // LLView functionality + virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); + virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); + virtual void draw( void ); + virtual bool handleKeyHere(KEY key, MASK mask); + + virtual bool handleAcceleratorKey(KEY key, MASK mask); + + virtual void onFocusLost(); + virtual void setFocus(bool b); +}; + +LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) : + LLMenuItemBranchGL(p) +{ +} + +// returns the normal width of this control in pixels - this is used +// for calculating the widest item, as well as for horizontal +// arrangement. +U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const +{ + U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS; + width += getFont()->getWidth( mLabel.getWString().c_str() ); + return width; +} + +// called to rebuild the draw label +void LLMenuItemBranchDownGL::buildDrawLabel( void ) +{ + mDrawAccelLabel.clear(); + std::string st = mDrawAccelLabel; + appendAcceleratorString( st ); + mDrawAccelLabel = st; +} + +void LLMenuItemBranchDownGL::openMenu( void ) +{ + LLMenuGL* branch = getBranch(); + if( branch->getVisible() && !branch->getTornOff() ) + { + branch->setVisible( false ); + } + else + { + if (branch->getTornOff()) + { + LLFloater * branch_parent = dynamic_cast(branch->getParent()); + if (branch_parent) + { + gFloaterView->bringToFront(branch_parent); + } + } + else + { + // We're showing the drop-down menu, so patch up its labels/rects + branch->arrange(); + + LLRect rect = branch->getRect(); + S32 left = 0; + S32 top = getRect().mBottom; + localPointToOtherView(left, top, &left, &top, branch->getParent()); + + rect.setLeftTopAndSize( left, top, + rect.getWidth(), rect.getHeight() ); + branch->setRect( rect ); + S32 x = 0; + S32 y = 0; + branch->localPointToScreen( 0, 0, &x, &y ); + S32 delta_x = 0; + + LLCoordScreen window_size; + LLWindow* windowp = getWindow(); + windowp->getSize(&window_size); + + S32 window_width = window_size.mX; + if( x > window_width - rect.getWidth() ) + { + delta_x = (window_width - rect.getWidth()) - x; + } + branch->translate( delta_x, 0 ); + + setHighlight(true); + branch->setVisible( true ); + branch->getParent()->sendChildToFront(branch); + } + } +} + +// set the hover status (called by it's menu) +void LLMenuItemBranchDownGL::setHighlight( bool highlight ) +{ + if (highlight == getHighlight()) + return; + + //NOTE: Purposely calling all the way to the base to bypass auto-open. + LLMenuItemGL::setHighlight(highlight); + + LLMenuGL* branch = getBranch(); + if (!branch) + return; + + if( !highlight) + { + if (branch->getTornOff()) + { + LLFloater * branch_parent = dynamic_cast(branch->getParent()); + if (branch_parent) + { + branch_parent->setFocus(false); + } + branch->clearHoverItem(); + } + else + { + branch->setVisible( false ); + } + } +} + +bool LLMenuItemBranchDownGL::isActive() const +{ + // for top level menus, being open is sufficient to be considered + // active, because clicking on them with the mouse will open + // them, without moving keyboard focus to them + return isOpen(); +} + +bool LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + // switch to mouse control mode + LLMenuGL::setKeyboardMode(false); + + if (getVisible() && isOpen()) + { + LLMenuGL::sMenuContainer->hideMenus(); + } + else + { + onCommit(); + } + + make_ui_sound("UISndClick"); + return true; +} + +bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + return true; +} + + +bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) +{ + bool branch_visible = getBranch()->getVisible(); + bool handled = getBranch()->handleAcceleratorKey(key, mask); + if (handled && !branch_visible && isInVisibleChain()) + { + // flash this menu entry because we triggered an invisible menu item + LLMenuHolderGL::setActivatedItem(this); + } + + return handled; +} +void LLMenuItemBranchDownGL::onFocusLost() +{ + // needed for tab-based selection + LLMenuItemBranchGL::onFocusLost(); + LLMenuGL::setKeyboardMode(false); + setHighlight(false); +} + +void LLMenuItemBranchDownGL::setFocus(bool b) +{ + // needed for tab-based selection + LLMenuItemBranchGL::setFocus(b); + LLMenuGL::setKeyboardMode(b); + setHighlight(b); +} + +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()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode())) + { + if (key == KEY_LEFT) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this); + // open new menu only if previous menu was open + if (itemp && itemp->getEnabled() && menu_open) + { + itemp->onCommit(); + } + + return true; + } + else if (key == KEY_RIGHT) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + LLMenuItemGL* itemp = getMenu()->highlightNextItem(this); + // open new menu only if previous menu was open + if (itemp && itemp->getEnabled() && menu_open) + { + itemp->onCommit(); + } + + return true; + } + else if (key == KEY_DOWN) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + if (!isActive()) + { + onCommit(); + } + getBranch()->highlightNextItem(NULL); + return true; + } + else if (key == KEY_UP) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + if (!isActive()) + { + onCommit(); + } + getBranch()->highlightPrevItem(NULL); + return true; + } + } + + return false; +} + +void LLMenuItemBranchDownGL::draw( void ) +{ + //FIXME: try removing this + if (getBranch()->getVisible() && !getBranch()->getTornOff()) + { + setHighlight(true); + } + + if( getHighlight() ) + { + gGL.color4fv( mHighlightBackground.get().mV ); + gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); + } + + LLColor4 color; + if (getHighlight()) + { + color = mHighlightForeground.get(); + } + else if( getEnabled() ) + { + color = mEnabledColor.get(); + } + else + { + color = mDisabledColor.get(); + } + getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color, + LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL); + + + // underline navigation key only when keyboard navigation has been initiated + if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) + { + std::string upper_case_label = mLabel.getString(); + LLStringUtil::toUpper(upper_case_label); + std::string::size_type offset = upper_case_label.find(getJumpKey()); + if (offset != std::string::npos) + { + S32 x_offset = ll_round((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f); + S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset); + S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1); + gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS); + } + } +} + + +class LLMenuScrollItem : public LLMenuItemCallGL +{ +public: + enum EArrowType + { + ARROW_DOWN, + ARROW_UP + }; + struct ArrowTypes : public LLInitParam::TypeValuesHelper + { + static void declareValues() + { + declare("up", ARROW_UP); + declare("down", ARROW_DOWN); + } + }; + + struct Params : public LLInitParam::Block + { + Optional arrow_type; + Optional scroll_callback; + }; + +protected: + LLMenuScrollItem(const Params&); + friend class LLUICtrlFactory; + +public: + /*virtual*/ void draw(); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent); + /*virtual*/ void setEnabled(bool enabled); + virtual void onCommit( void ); + +private: + LLButton* mArrowBtn; +}; + +LLMenuScrollItem::LLMenuScrollItem(const Params& p) +: LLMenuItemCallGL(p) +{ + std::string icon; + if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP) + { + icon = "arrow_up.tga"; + } + else + { + icon = "arrow_down.tga"; + } + + LLButton::Params bparams; + + // Disabled the Return key handling by LLMenuScrollItem instead of + // passing the key press to the currently selected menu item. See STORM-385. + bparams.commit_on_return(false); + bparams.mouse_opaque(true); + bparams.scale_image(false); + bparams.click_callback(p.scroll_callback); + bparams.mouse_held_callback(p.scroll_callback); + bparams.follows.flags(FOLLOWS_ALL); + std::string background = "transparent.j2c"; + bparams.image_unselected.name(background); + bparams.image_disabled.name(background); + bparams.image_selected.name(background); + bparams.image_hover_selected.name(background); + bparams.image_disabled_selected.name(background); + bparams.image_hover_unselected.name(background); + bparams.image_overlay.name(icon); + + mArrowBtn = LLUICtrlFactory::create(bparams); + addChild(mArrowBtn); +} + +/*virtual*/ +void LLMenuScrollItem::draw() +{ + LLUICtrl::draw(); +} + +/*virtual*/ +void LLMenuScrollItem::reshape(S32 width, S32 height, bool called_from_parent) +{ + mArrowBtn->reshape(width, height, called_from_parent); + LLView::reshape(width, height, called_from_parent); +} + +/*virtual*/ +void LLMenuScrollItem::setEnabled(bool enabled) +{ + mArrowBtn->setEnabled(enabled); + LLView::setEnabled(enabled); +} + +void LLMenuScrollItem::onCommit( void ) +{ + LLUICtrl::onCommit(); +} + +///============================================================================ +/// Class LLMenuGL +///============================================================================ + +LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) +: LLUICtrl(p), + mBackgroundColor( p.bg_color() ), + mBgVisible( p.bg_visible ), + mDropShadowed( p.drop_shadow ), + mHasSelection(false), + mHorizontalLayout( p.horizontal_layout ), + mScrollable(mHorizontalLayout ? false : p.scrollable), // Scrolling is supported only for vertical layout + mMaxScrollableItems(p.max_scrollable_items), + mPreferredWidth(p.preferred_width), + mKeepFixedSize( p.keep_fixed_size ), + mLabel (p.label), + mLastMouseX(0), + mLastMouseY(0), + mMouseVelX(0), + mMouseVelY(0), + mTornOff(false), + mTearOffItem(NULL), + mSpilloverBranch(NULL), + mFirstVisibleItem(NULL), + mArrowUpItem(NULL), + mArrowDownItem(NULL), + mSpilloverMenu(NULL), + mJumpKey(p.jump_key), + mCreateJumpKeys(p.create_jump_keys), + mNeedsArrange(false), + mAlwaysShowMenu(false), + mResetScrollPositionOnShow(true), + mShortcutPad(p.shortcut_pad), + mFont(p.font) +{ + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("_"); + tokenizer tokens(p.label(), sep); + tokenizer::iterator token_iter; + + S32 token_count = 0; + std::string new_menu_label; + for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + new_menu_label += (*token_iter); + if (token_count > 0) + { + setJumpKey((*token_iter).c_str()[0]); + } + ++token_count; + } + setLabel(new_menu_label); + + mFadeTimer.stop(); +} + +void LLMenuGL::initFromParams(const LLMenuGL::Params& p) +{ + LLUICtrl::initFromParams(p); + setCanTearOff(p.can_tear_off); +} + +// Destroys the object +LLMenuGL::~LLMenuGL( void ) +{ + // delete the branch, as it might not be in view hierarchy + // leave the menu, because it is always in view hierarchy + delete mSpilloverBranch; + mJumpKeys.clear(); +} + +void LLMenuGL::setCanTearOff(bool tear_off) +{ + if (tear_off && mTearOffItem == NULL) + { + LLMenuItemTearOffGL::Params p; + mTearOffItem = LLUICtrlFactory::create(p); + addChild(mTearOffItem); + } + else if (!tear_off && mTearOffItem != NULL) + { + mItems.remove(mTearOffItem); + removeChild(mTearOffItem); + delete mTearOffItem; + mTearOffItem = NULL; + needsArrange(); + } +} + +bool LLMenuGL::addChild(LLView* view, S32 tab_group) +{ + LLMenuGL* menup = dynamic_cast(view); + if (menup) + { + return appendMenu(menup); + } + + LLMenuItemGL* itemp = dynamic_cast(view); + if (itemp) + { + return append(itemp); + } + + return false; +} + +// Used in LLContextMenu and in LLTogleableMenu + +// Add an item to the context menu branch +bool LLMenuGL::addContextChild(LLView* view, S32 tab_group) +{ + LLContextMenu* context = dynamic_cast(view); + if (context) + { + return appendContextSubMenu(context); + } + + LLMenuItemSeparatorGL* separator = dynamic_cast(view); + if (separator) + { + return append(separator); + } + + LLMenuItemGL* item = dynamic_cast(view); + if (item) + { + return append(item); + } + + LLMenuGL* menup = dynamic_cast(view); + if (menup) + { + return appendMenu(menup); + } + + return false; +} + + +void LLMenuGL::deleteAllChildren() +{ + mItems.clear(); + LLUICtrl::deleteAllChildren(); +} + +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); +} + +bool LLMenuGL::postBuild() +{ + createJumpKeys(); + return LLUICtrl::postBuild(); +} + +// 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() +{ + LLMenuItemGL* highlighted_item = getHighlightedItem(); + bool active = getVisible() && getEnabled(); + + if (active) + { + if (getTornOff()) + { + // activation of jump keys on torn off menus controlled by keyboard focus + LLFloater * parent = dynamic_cast(getParent()); + if (parent) + { + active = parent->hasFocus(); + } + } + else + { + // Are we the terminal active menu? + // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus) + // and we don't have a highlighted menu item pointing to an active sub-menu + active = (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active... + && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active + } + } + + return active; +} + +bool LLMenuGL::isOpen() +{ + if (getTornOff()) + { + LLMenuItemGL* itemp = getHighlightedItem(); + // if we have an open sub-menu, then we are considered part of + // the open menu chain even if we don't have focus + if (itemp && itemp->isOpen()) + { + return true; + } + // otherwise we are only active if we have keyboard focus + LLFloater * parent = dynamic_cast(getParent()); + if (parent) + { + return parent->hasFocus(); + } + return false; + } + else + { + // normally, menus are hidden as soon as the user focuses + // on another menu, so just use the visibility criterion + return getVisible(); + } +} + + + +bool LLMenuGL::scrollItems(EScrollingDirection direction) +{ + // Slowing down items scrolling when arrow button is held + if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem) + { + mScrollItemsTimer.setTimerExpirySec(.033f); + } + else + { + return false; + } + + switch (direction) + { + case SD_UP: + { + item_list_t::iterator cur_item_iter; + item_list_t::iterator prev_item_iter; + for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) + { + if( (*cur_item_iter) == mFirstVisibleItem) + { + break; + } + if ((*cur_item_iter)->getVisible()) + { + prev_item_iter = cur_item_iter; + } + } + + if ((*prev_item_iter)->getVisible()) + { + mFirstVisibleItem = *prev_item_iter; + } + break; + } + case SD_DOWN: + { + if (NULL == mFirstVisibleItem) + { + mFirstVisibleItem = *mItems.begin(); + } + + 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) == mFirstVisibleItem) + { + break; + } + } + + item_list_t::iterator next_item_iter; + + if (cur_item_iter != mItems.end()) + { + for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) + { + if( (*next_item_iter)->getVisible()) + { + break; + } + } + + if (next_item_iter != mItems.end() && + (*next_item_iter)->getVisible()) + { + mFirstVisibleItem = *next_item_iter; + } + } + break; + } + case SD_BEGIN: + { + mFirstVisibleItem = *mItems.begin(); + break; + } + case SD_END: + { + item_list_t::reverse_iterator first_visible_item_iter = mItems.rend(); + + // Need to scroll through number of actual existing items in menu. + // Otherwise viewer will hang for a time needed to scroll U32_MAX + // times in std::advance(). STORM-659. + size_t nitems = mItems.size(); + U32 scrollable_items = nitems < mMaxScrollableItems ? nitems : mMaxScrollableItems; + + // Advance by mMaxScrollableItems back from the end of the list + // to make the last item visible. + std::advance(first_visible_item_iter, scrollable_items); + mFirstVisibleItem = *first_visible_item_iter; + break; + } + default: + LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL; + } + + mNeedsArrange = true; + arrangeAndClear(); + + return true; +} + +// rearrange the child rects so they fit the shape of the menu. +void LLMenuGL::arrange( void ) +{ + // calculate the height & width, and set our rect based on that + // information. + const LLRect& initial_rect = getRect(); + + U32 width = 0, height = MENU_ITEM_PADDING; + + cleanupSpilloverBranch(); + + if( mItems.size() ) + { + const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0); + + // 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(); + + // *FIX: create the item first and then ask for its dimensions? + S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate + S32 spillover_item_height = 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 != mSpilloverBranch + && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width) + { + // no room for any more items + createSpilloverBranch(); + + 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->addChild(itemp); + } + + addChild(mSpilloverBranch); + + height = llmax(height, mSpilloverBranch->getNominalHeight()); + width += mSpilloverBranch->getNominalWidth(); + + break; + } + else + { + // track our rect + height = llmax(height, (*item_iter)->getNominalHeight()); + width += (*item_iter)->getNominalWidth(); + } + } + } + } + else + { + for (LLMenuItemGL* itemp : mItems) + { + // do first so LLMenuGLItemCall can call on_visible to determine if visible + itemp->buildDrawLabel(); + } + item_list_t::iterator item_iter; + + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + if ((*item_iter)->getVisible()) + { + if (!getTornOff() + && !mScrollable + && *item_iter != mSpilloverBranch + && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height) + { + // don't show only one item + int visible_items = 0; + item_list_t::iterator count_iter; + for (count_iter = item_iter; count_iter != mItems.end(); ++count_iter) + { + if((*count_iter)->getVisible()) + visible_items++; + } + if (visible_items>1) + { + // no room for any more items + createSpilloverBranch(); + + 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->addChild(itemp); + } + + + addChild(mSpilloverBranch); + + height += mSpilloverBranch->getNominalHeight(); + width = llmax( width, mSpilloverBranch->getNominalWidth() ); + + break; + } + } + + // track our rect + height += (*item_iter)->getNominalHeight(); + width = llmax( width, (*item_iter)->getNominalWidth() ); + + if (mScrollable) + { + // Determining visible items boundaries + if (NULL == mFirstVisibleItem) + { + mFirstVisibleItem = *item_iter; + } + + if (*item_iter == mFirstVisibleItem) + { + height_before_first_visible_item = height - (*item_iter)->getNominalHeight(); + first_visible_item_iter = item_iter; + scrollable_items_cnt = 0; + } + + if (-1 != height_before_first_visible_item && 0 == visible_items_height && + (++scrollable_items_cnt > mMaxScrollableItems || + height - height_before_first_visible_item > max_height - spillover_item_height * 2 )) + { + first_hidden_item_iter = item_iter; + visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight(); + scrollable_items_cnt--; + } + } + } + } + + if (mPreferredWidth < U32_MAX) + width = llmin(mPreferredWidth, max_width); + + if (mScrollable) + { + S32 max_items_height = max_height - spillover_item_height * 2; + + if (visible_items_height == 0) + visible_items_height = height - height_before_first_visible_item; + + // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit + if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems) + { + item_list_t::iterator tmp_iter(first_visible_item_iter); + while (visible_items_height < max_items_height && + scrollable_items_cnt < mMaxScrollableItems && + first_visible_item_iter != mItems.begin()) + { + if ((*first_visible_item_iter)->getVisible()) + { + // It keeps visible item, after first_visible_item_iter + tmp_iter = first_visible_item_iter; + } + + first_visible_item_iter--; + + if ((*first_visible_item_iter)->getVisible()) + { + visible_items_height += (*first_visible_item_iter)->getNominalHeight(); + height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight(); + scrollable_items_cnt++; + } + } + + // Roll back one item, that doesn't fit + if (visible_items_height > max_items_height) + { + visible_items_height -= (*first_visible_item_iter)->getNominalHeight(); + height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight(); + scrollable_items_cnt--; + first_visible_item_iter = tmp_iter; + } + if (!(*first_visible_item_iter)->getVisible()) + { + first_visible_item_iter = tmp_iter; + } + + mFirstVisibleItem = *first_visible_item_iter; + } + } + } + + S32 cur_height = (S32)llmin(max_height, height); + + if (mScrollable && + (height_before_first_visible_item > MENU_ITEM_PADDING || + height_before_first_visible_item + visible_items_height < (S32)height)) + { + // Reserving 2 extra slots for arrow items + cur_height = visible_items_height + spillover_item_height * 2; + } + + setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height)); + + S32 cur_width = 0; + S32 offset = 0; + if (mScrollable) + { + // No space for all items, creating arrow items + if (height_before_first_visible_item > MENU_ITEM_PADDING || + height_before_first_visible_item + visible_items_height < (S32)height) + { + if (NULL == mArrowUpItem) + { + LLMenuScrollItem::Params item_params; + item_params.name(ARROW_UP); + item_params.arrow_type(LLMenuScrollItem::ARROW_UP); + item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP)); + + mArrowUpItem = LLUICtrlFactory::create(item_params); + LLUICtrl::addChild(mArrowUpItem); + + } + if (NULL == mArrowDownItem) + { + LLMenuScrollItem::Params item_params; + item_params.name(ARROW_DOWN); + item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN); + item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN)); + + mArrowDownItem = LLUICtrlFactory::create(item_params); + LLUICtrl::addChild(mArrowDownItem); + } + + LLRect rect; + mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight())); + mArrowUpItem->setVisible(true); + mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING); + mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight()); + mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight())); + mArrowDownItem->setVisible(true); + mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height); + mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight()); + + cur_height -= mArrowUpItem->getNominalHeight(); + + offset = menu_region_rect.mRight; // This moves items behind visible area + } + else + { + if (NULL != mArrowUpItem) + { + mArrowUpItem->setVisible(false); + } + if (NULL != mArrowDownItem) + { + mArrowDownItem->setVisible(false); + } + } + + } + + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + if ((*item_iter)->getVisible()) + { + if (mScrollable) + { + if (item_iter == first_visible_item_iter) + { + offset = 0; + } + else if (item_iter == first_hidden_item_iter) + { + offset = menu_region_rect.mRight; // This moves items behind visible area + } + } + + // setup item rect to hold label + LLRect rect; + if (mHorizontalLayout) + { + rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height); + cur_width += (*item_iter)->getNominalWidth(); + } + else + { + rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight()); + if (offset == 0) + { + cur_height -= (*item_iter)->getNominalHeight(); + } + } + (*item_iter)->setRect( rect ); + } + } + + + if (getTornOff()) + { + LLTearOffMenu * torn_off_menu = dynamic_cast(getParent()); + if (torn_off_menu) + { + torn_off_menu->updateSize(); + } + } + } + if (mKeepFixedSize) + { + reshape(initial_rect.getWidth(), initial_rect.getHeight()); + } +} + +void LLMenuGL::arrangeAndClear( void ) +{ + if (mNeedsArrange) + { + arrange(); + mNeedsArrange = false; + } +} + +void LLMenuGL::createSpilloverBranch() +{ + if (!mSpilloverBranch) + { + // should be NULL but delete anyway + delete mSpilloverMenu; + // technically, you can't tear off spillover menus, but we're passing the handle + // along just to be safe + LLMenuGL::Params p; + std::string label = LLTrans::getString("More"); + p.name("More"); + p.label(label); + p.bg_color(mBackgroundColor); + p.bg_visible(true); + p.can_tear_off(false); + mSpilloverMenu = new LLMenuGL(p); + mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer); + + LLMenuItemBranchGL::Params branch_params; + branch_params.name = "More"; + branch_params.label = label; + branch_params.branch = mSpilloverMenu; + branch_params.font.style = "italic"; + branch_params.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); + branch_params.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); + branch_params.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); + + mSpilloverBranch = LLUICtrlFactory::create(branch_params); + } +} + +void LLMenuGL::cleanupSpilloverBranch() +{ + if (mSpilloverBranch && mSpilloverBranch->getParent() == this) + { + // head-recursion to propagate items back up to root menu + mSpilloverMenu->cleanupSpilloverBranch(); + + // pop off spillover items + while (mSpilloverMenu->getItemCount()) + { + LLMenuItemGL* itemp = mSpilloverMenu->getItem(0); + mSpilloverMenu->removeChild(itemp); + // put them at the end of our own list + addChild(itemp); + } + + // Delete the branch, and since the branch will delete the menu, + // set the menu* to null. + delete mSpilloverBranch; + mSpilloverBranch = NULL; + mSpilloverMenu = NULL; + } +} + +void LLMenuGL::createJumpKeys() +{ + if (!mCreateJumpKeys) return; + mCreateJumpKeys = false; + + mJumpKeys.clear(); + + std::set unique_words; + std::set shared_words; + + item_list_t::iterator item_it; + typedef boost::tokenizer > tokenizer; + boost::char_separator sep(" "); + + for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) + { + std::string uppercase_label = (*item_it)->getLabel(); + LLStringUtil::toUpper(uppercase_label); + + tokenizer tokens(uppercase_label, sep); + tokenizer::iterator token_iter; + for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + if (unique_words.find(*token_iter) != unique_words.end()) + { + // this word exists in more than one menu instance + shared_words.insert(*token_iter); + } + else + { + // we have a new word, keep track of it + unique_words.insert(*token_iter); + } + } + } + + // pre-assign specified jump keys + for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) + { + KEY jump_key = (*item_it)->getJumpKey(); + if(jump_key != KEY_NONE) + { + if (mJumpKeys.find(jump_key) == mJumpKeys.end()) + { + mJumpKeys.insert(std::pair(jump_key, (*item_it))); + } + else + { + // this key is already spoken for, + // so we need to reassign it below + (*item_it)->setJumpKey(KEY_NONE); + } + } + } + + for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) + { + // skip over items that already have assigned jump keys + if ((*item_it)->getJumpKey() != KEY_NONE) + { + continue; + } + std::string uppercase_label = (*item_it)->getLabel(); + LLStringUtil::toUpper(uppercase_label); + + tokenizer tokens(uppercase_label, sep); + tokenizer::iterator token_iter; + + bool found_key = false; + for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + std::string uppercase_word = *token_iter; + + // this word is not shared with other menu entries... + if (shared_words.find(*token_iter) == shared_words.end()) + { + S32 i; + for(i = 0; i < (S32)uppercase_word.size(); i++) + { + char jump_key = uppercase_word[i]; + + if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) && + mJumpKeys.find(jump_key) == mJumpKeys.end())) + { + mJumpKeys.insert(std::pair(jump_key, (*item_it))); + (*item_it)->setJumpKey(jump_key); + found_key = true; + break; + } + } + } + if (found_key) + { + break; + } + } + } +} + +// remove all items on the menu +void LLMenuGL::empty( void ) +{ + cleanupSpilloverBranch(); + + mItems.clear(); + mFirstVisibleItem = NULL; + mArrowUpItem = NULL; + mArrowDownItem = NULL; + + deleteAllChildren(); +} + +// erase group of items from menu +void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/) +{ + S32 items = mItems.size(); + + if ( items == 0 || begin >= end || begin < 0 || end > items ) + { + return; + } + + item_list_t::iterator start_position = mItems.begin(); + std::advance(start_position, begin); + + item_list_t::iterator end_position = mItems.begin(); + std::advance(end_position, end); + + for (item_list_t::iterator position_iter = start_position; position_iter != end_position; position_iter++) + { + LLUICtrl::removeChild(*position_iter); + } + + mItems.erase(start_position, end_position); + + if (arrange) + { + needsArrange(); + } +} + +// add new item at position +void LLMenuGL::insert( S32 position, LLView * ctrl, bool arrange /*= true*/ ) +{ + LLMenuItemGL * item = dynamic_cast(ctrl); + + if (NULL == item || position < 0 || position >= mItems.size()) + { + return; + } + + item_list_t::iterator position_iter = mItems.begin(); + std::advance(position_iter, position); + mItems.insert(position_iter, item); + LLUICtrl::addChild(item); + + if (arrange) + { + needsArrange(); + } +} + +// Adjust rectangle of the menu +void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom) +{ + setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom)); + needsArrange(); +} + +bool LLMenuGL::handleJumpKey(KEY key) +{ + // must perform case-insensitive comparison, so just switch to uppercase input key + key = toupper(key); + navigation_key_map_t::iterator found_it = mJumpKeys.find(key); + if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + // force highlight to close old menus and open and sub-menus + found_it->second->setHighlight(true); + found_it->second->onCommit(); + + } + // if we are navigating the menus, we need to eat the keystroke + // so rest of UI doesn't handle it + return true; +} + + +// Add the menu item to this menu. +bool LLMenuGL::append( LLMenuItemGL* item ) +{ + if (!item) return false; + mItems.push_back( item ); + LLUICtrl::addChild(item); + needsArrange(); + return true; +} + +// add a separator to this menu +bool LLMenuGL::addSeparator() +{ + LLMenuItemSeparatorGL::Params p; + LLMenuItemGL* separator = LLUICtrlFactory::create(p); + return addChild(separator); +} + +// add a menu - this will create a cascading menu +bool LLMenuGL::appendMenu( LLMenuGL* menu ) +{ + if( menu == this ) + { + LL_ERRS() << "** Attempt to attach menu to itself. This is certainly " + << "a logic error." << LL_ENDL; + } + bool success = true; + + LLMenuItemBranchGL::Params p; + p.name = menu->getName(); + p.label = menu->getLabel(); + p.branch = menu; + p.jump_key = menu->getJumpKey(); + p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); + p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); + p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); + p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); + + LLMenuItemBranchGL* branch = LLUICtrlFactory::create(p); + success &= append( branch ); + + // Inherit colors + menu->setBackgroundColor( mBackgroundColor ); + menu->updateParent(LLMenuGL::sMenuContainer); + return success; +} + +// add a context menu branch +bool LLMenuGL::appendContextSubMenu(LLMenuGL *menu) +{ + if (menu == this) + { + LL_ERRS() << "Can't attach a context menu to itself" << LL_ENDL; + } + + LLContextMenuBranch *item; + LLContextMenuBranch::Params p; + p.name = menu->getName(); + p.label = menu->getLabel(); + p.branch = (LLContextMenu *)menu; + p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); + p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); + p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); + p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); + + item = LLUICtrlFactory::create(p); + LLMenuGL::sMenuContainer->addChild(item->getBranch()); + + return append( item ); +} + +void LLMenuGL::setEnabledSubMenus(bool enable) +{ + setEnabled(enable); + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + (*item_iter)->setEnabledSubMenus( enable ); + } +} + +// setItemEnabled() - pass the label and the enable flag for a menu +// item. true will make sure it's enabled, false will disable it. +void LLMenuGL::setItemEnabled( const std::string& name, bool enable ) +{ + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + if( (*item_iter)->getName() == name ) + { + (*item_iter)->setEnabled( enable ); + (*item_iter)->setEnabledSubMenus( enable ); + break; + } + } +} + +void LLMenuGL::setItemVisible( const std::string& name, bool visible ) +{ + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + if( (*item_iter)->getName() == name ) + { + (*item_iter)->setVisible( visible ); + needsArrange(); + break; + } + } +} + + +void LLMenuGL::setItemLabel(const std::string &name, const std::string &label) +{ + LLMenuItemGL *item = getItem(name); + + if (item) + item->setLabel(label); +} + +void LLMenuGL::setItemLastSelected(LLMenuItemGL* item) +{ + if (getVisible()) + { + LLMenuHolderGL::setActivatedItem(item); + } + + // update enabled and checkmark status + item->buildDrawLabel(); +} + +// Set whether drop shadowed +void LLMenuGL::setDropShadowed( const bool shadowed ) +{ + mDropShadowed = shadowed; +} + +void LLMenuGL::setTornOff(bool torn_off) +{ + mTornOff = torn_off; +} + +U32 LLMenuGL::getItemCount() +{ + return mItems.size(); +} + +LLMenuItemGL* LLMenuGL::getItem(S32 number) +{ + if (number >= 0 && number < (S32)mItems.size()) + { + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + if (number == 0) + { + return (*item_iter); + } + number--; + } + } + return NULL; +} + +LLMenuItemGL* LLMenuGL::getItem(std::string name) +{ + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + if ((*item_iter)->getName() == name) + { + return (*item_iter); + } + } + return NULL; +} + +LLMenuItemGL* LLMenuGL::getHighlightedItem() +{ + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + if ((*item_iter)->getHighlight()) + { + return (*item_iter); + } + } + return NULL; +} + +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()) + { + LLFloater * parent = dynamic_cast(getParent()); + if (parent) + { + parent->setFocus(true); + } + } + + // 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()) + { + next_item_iter = mItems.begin(); + } + else + { + 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(); + + // If current item is the last in the list, the menu is scrolled to the beginning + // and the first item is highlighted. + if (mScrollable && !scrollItems(SD_BEGIN)) + { + return NULL; + } + } + // If current item is the last visible, the menu is scrolled one item down + // and the next item is highlighted. + else if (mScrollable && + (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems) + { + // Call highlightNextItem() recursively only if the menu was successfully scrolled down. + // If scroll timer hasn't expired yet the menu won't be scrolled and calling + // highlightNextItem() will result in an endless recursion. + if (scrollItems(SD_DOWN)) + { + return highlightNextItem(cur_item, skip_disabled); + } + else + { + return NULL; + } + } + } + + // when first highlighting a menu, skip over tear off menu item + if (mTearOffItem && !cur_item) + { + // we know the first item is the tear off menu item + cur_item_iter = mItems.begin(); + next_item_iter++; + if (next_item_iter == mItems.end()) + { + next_item_iter = mItems.begin(); + } + } + + while(1) + { + // skip separators and disabled/invisible items + if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast(*next_item_iter)) + { + if (cur_item) + { + cur_item->setHighlight(false); + } + (*next_item_iter)->setHighlight(true); + return (*next_item_iter); + } + + + if (!skip_disabled || next_item_iter == cur_item_iter) + { + break; + } + + next_item_iter++; + if (next_item_iter == mItems.end()) + { + if (cur_item_iter == mItems.end()) + { + break; + } + next_item_iter = mItems.begin(); + } + } + + return NULL; +} + +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()) + { + LLFloater * parent = dynamic_cast(getParent()); + if (parent) + { + parent->setFocus(true); + } + } + + // 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()) + { + prev_item_iter = mItems.rbegin(); + } + else + { + 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(); + + // If current item is the first in the list, the menu is scrolled to the end + // and the last item is highlighted. + if (mScrollable && !scrollItems(SD_END)) + { + return NULL; + } + } + // If current item is the first visible, the menu is scrolled one item up + // and the previous item is highlighted. + else if (mScrollable && + std::distance(first_visible_item_iter, cur_item_iter) <= 0) + { + // Call highlightNextItem() only if the menu was successfully scrolled up. + // If scroll timer hasn't expired yet the menu won't be scrolled and calling + // highlightNextItem() will result in an endless recursion. + if (scrollItems(SD_UP)) + { + return highlightPrevItem(cur_item, skip_disabled); + } + else + { + return NULL; + } + } + } + + while(1) + { + // skip separators and disabled/invisible items + if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME) + { + (*prev_item_iter)->setHighlight(true); + return (*prev_item_iter); + } + + if (!skip_disabled || prev_item_iter == cur_item_iter) + { + break; + } + + prev_item_iter++; + if (prev_item_iter == mItems.rend()) + { + if (cur_item_iter == mItems.rend()) + { + break; + } + + prev_item_iter = mItems.rbegin(); + } + } + + return NULL; +} + +void LLMenuGL::buildDrawLabels() +{ + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + (*item_iter)->buildDrawLabel(); + } +} + +void LLMenuGL::updateParent(LLView* parentp) +{ + if (getParent()) + { + getParent()->removeChild(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); + } +} + +bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + if (key == KEY_NONE) + { + return false; + } + // Note: checking this way because mAccelerators seems to be broken + // mAccelerators probably needs to be cleaned up or fixed + // It was used for dupplicate accelerator avoidance. + item_list_t::const_iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + LLMenuItemGL* itemp = *item_iter; + if (itemp->hasAccelerator(key, mask)) + { + return true; + } + } + return false; +} + +bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) +{ + // don't handle if not enabled + if(!getEnabled()) + { + return false; + } + + // Pass down even if not visible + item_list_t::iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + LLMenuItemGL* itemp = *item_iter; + if (itemp->handleAcceleratorKey(key, mask)) + { + return true; + } + } + + return false; +} + +bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char ) +{ + if (jumpKeysActive()) + { + return handleJumpKey((KEY)uni_char); + } + return false; +} + +bool LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) +{ + // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU + bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; + S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; + S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; + LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y); + mouse_dir.normVec(); + LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY); + mouse_avg_dir.normVec(); + F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f)); + mMouseVelX = ll_round(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp)); + mMouseVelY = ll_round(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp)); + mLastMouseX = x; + mLastMouseY = y; + + // don't change menu focus unless mouse is moving or alt key is not held down + if ((llabs(mMouseVelX) > 0 || + llabs(mMouseVelY) > 0) && + (!mHasSelection || + //(mouse_delta_x == 0 && mouse_delta_y == 0) || + (mMouseVelX < 0) || + 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) + { + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) + { + // moving mouse always highlights new item + if (mouse_delta_x != 0 || mouse_delta_y != 0) + { + ((LLMenuItemGL*)viewp)->setHighlight(false); + } + } + } + + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + //RN: always call handleHover to track mGotHover status + // but only set highlight when mouse is moving + if( viewp->getVisible() && + //RN: allow disabled items to be highlighted to preserve "active" menus when + // moving mouse through them + //viewp->getEnabled() && + viewp->pointInView(local_x, local_y) && + viewp->handleHover(local_x, local_y, mask)) + { + // moving mouse always highlights new item + if (mouse_delta_x != 0 || mouse_delta_y != 0) + { + ((LLMenuItemGL*)viewp)->setHighlight(true); + LLMenuGL::setKeyboardMode(false); + } + 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 ) +{ + if (!mScrollable) + return blockMouseEvent(x, y); + + if( clicks > 0 ) + { + while( clicks-- ) + scrollItems(SD_DOWN); + } + else + { + while( clicks++ ) + scrollItems(SD_UP); + } + + return true; +} + + +void LLMenuGL::draw( void ) +{ + if (mNeedsArrange) + { + arrange(); + mNeedsArrange = false; + } + if (mDropShadowed && !mTornOff) + { + static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow"); + gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, + color_drop_shadow, DROP_SHADOW_FLOATER); + } + + if( mBgVisible ) + { + gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor.get() ); + } + LLView::draw(); +} + +void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha) +{ + LLColor4 color = itemp->getHighlightBgColor() % alpha; + gGL.color4fv( color.mV ); + LLRect item_rect = itemp->getRect(); + gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0); +} + +void LLMenuGL::setVisible(bool visible) +{ + if (visible != getVisible()) + { + if (!visible) + { + mFadeTimer.start(); + clearHoverItem(); + // reset last known mouse coordinates so + // we don't spoof a mouse move next time we're opened + mLastMouseX = 0; + mLastMouseY = 0; + } + else + { + mHasSelection = true; + mFadeTimer.stop(); + } + + LLView::setVisible(visible); + } +} + +LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, bool recurse) const +{ + LLView* view = findChildView(name, recurse); + if (view) + { + LLMenuItemBranchGL* branch = dynamic_cast(view); + if (branch) + { + return branch->getBranch(); + } + + LLMenuGL* menup = dynamic_cast(view); + if (menup) + { + return menup; + } + } + LL_WARNS() << "Child Menu " << name << " not found in menu " << getName() << LL_ENDL; + return NULL; +} + +bool LLMenuGL::clearHoverItem() +{ + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it; + if (itemp->getHighlight()) + { + itemp->setHighlight(false); + return true; + } + } + return false; +} + +void hide_top_view( LLView* view ) +{ + if( view ) view->setVisible( false ); +} + + +// 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, S32 mouse_x, S32 mouse_y) +{ + const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size + const S32 CURSOR_WIDTH = 12; + + if (menu->getChildList()->empty()) + { + return; + } + + menu->setVisible( true ); + + if(!menu->getAlwaysShowMenu()) + { + //Do not show menu if all menu items are disabled + bool item_enabled = false; + for (LLView::child_list_t::const_iterator itor = menu->getChildList()->begin(); + itor != menu->getChildList()->end(); + ++itor) + { + LLView *menu_item = (*itor); + item_enabled = item_enabled || menu_item->getEnabled(); + } + + if(!item_enabled) + { + menu->setVisible( false ); + return; + } + } + + // Resetting scrolling position + if (menu->isScrollable() && menu->isScrollPositionOnShowReset()) + { + menu->mFirstVisibleItem = NULL; + } + + // Fix menu rect if needed. + menu->needsArrange(); + menu->arrangeAndClear(); + + if ((mouse_x == 0) || (mouse_y == 0)) + + { + // 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() + + LLUI::getInstance()->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(); + S32 left = x + HPAD; + S32 top = y; + spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); + rect.setLeftTopAndSize( left, top, + rect.getWidth(), rect.getHeight() ); + menu->setRect( rect ); + + + // 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 ); + if (menu->getRect().mTop > menu_region_rect.mTop) + { + // not enough space: align with top, ignore exclusion + menu->translateIntoRect( menu_region_rect ); + } + menu->getParent()->sendChildToFront(menu); +} + +///============================================================================ +/// Class LLMenuBarGL +///============================================================================ + +static LLDefaultChildRegistry::Register r2("menu_bar"); + +LLMenuBarGL::LLMenuBarGL( const Params& p ) +: LLMenuGL(p), + mAltKeyTrigger(false) +{} + +// Default destructor +LLMenuBarGL::~LLMenuBarGL() +{ + std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer()); + mAccelerators.clear(); +} + +bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) +{ + if (getHighlightedItem() && mask == MASK_NONE) + { + // unmodified key accelerators are ignored when navigating menu + // (but are used as jump keys so will still work when appropriate menu is up) + return false; + } + bool result = LLMenuGL::handleAcceleratorKey(key, mask); + if (result && mask & MASK_ALT) + { + // ALT key used to trigger hotkey, don't use as shortcut to open menu + mAltKeyTrigger = false; + } + + if(!result + && (key == KEY_F10 && mask == MASK_CONTROL) + && !gKeyboard->getKeyRepeated(key) + && isInVisibleChain()) + { + if (getHighlightedItem()) + { + clearHoverItem(); + LLMenuGL::setKeyboardMode(false); + } + else + { + // close menus originating from other menu bars when first opening menu via keyboard + LLMenuGL::sMenuContainer->hideMenus(); + highlightNextItem(NULL); + LLMenuGL::setKeyboardMode(true); + } + return true; + } + + if (result && !getHighlightedItem() && LLMenuGL::sMenuContainer->hasVisibleMenu()) + { + // close menus originating from other menu bars + LLMenuGL::sMenuContainer->hideMenus(); + } + + return result; +} + +bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask) +{ + static LLUICachedControl use_altkey_for_menus ("UseAltKeyForMenus", 0); + if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus) + { + mAltKeyTrigger = true; + } + else // if any key other than ALT hit, clear out waiting for Alt key mode + { + mAltKeyTrigger = false; + } + + if (key == KEY_ESCAPE && mask == MASK_NONE) + { + LLMenuGL::setKeyboardMode(false); + // if any menus are visible, this will return true, stopping further processing of ESCAPE key + return LLMenuGL::sMenuContainer->hideMenus(); + } + + // before processing any other key, check to see if ALT key has triggered menu access + checkMenuTrigger(); + + return LLMenuGL::handleKeyHere(key, mask); +} + +bool LLMenuBarGL::handleJumpKey(KEY key) +{ + // perform case-insensitive comparison + key = toupper(key); + navigation_key_map_t::iterator found_it = mJumpKeys.find(key); + if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) + { + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(true); + + found_it->second->setHighlight(true); + found_it->second->onCommit(); + } + return true; +} + +bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // 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()) + { + LLMenuGL::sMenuContainer->hideMenus(); + } + + return LLMenuGL::handleMouseDown(x, y, mask); +} + +bool LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + return LLMenuGL::handleMouseDown(x, y, mask); +} + +void LLMenuBarGL::draw() +{ + LLMenuItemGL* itemp = getHighlightedItem(); + // If we are in mouse-control mode and the mouse cursor is not hovering over + // the current highlighted menu item and it isn't open, then remove the + // highlight. This is done via a polling mechanism here, as we don't receive + // notifications when the mouse cursor moves off of us + if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode()) + { + clearHoverItem(); + } + + checkMenuTrigger(); + + LLMenuGL::draw(); +} + + +void LLMenuBarGL::checkMenuTrigger() +{ + // has the ALT key been pressed and subsequently released? + if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT)) + { + // if alt key was released quickly, treat it as a menu access key + // otherwise it was probably an Alt-zoom or similar action + static LLUICachedControl menu_access_key_time ("MenuAccessKeyTime", 0); + if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= menu_access_key_time || + gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2) + { + if (getHighlightedItem()) + { + clearHoverItem(); + } + else + { + // close menus originating from other menu bars + LLMenuGL::sMenuContainer->hideMenus(); + + highlightNextItem(NULL); + LLMenuGL::setKeyboardMode(true); + } + } + mAltKeyTrigger = false; + } +} + +bool LLMenuBarGL::jumpKeysActive() +{ + // require user to be in keyboard navigation mode to activate key triggers + // as menu bars are always visible and it is easy to leave the mouse cursor over them + return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive(); +} + +// rearrange the child rects so they fit the shape of the menu bar. +void LLMenuBarGL::arrange( void ) +{ + U32 pos = 0; + LLRect rect( 0, getRect().getHeight(), 0, 0 ); + item_list_t::const_iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + LLMenuItemGL* item = *item_iter; + if (item->getVisible()) + { + rect.mLeft = pos; + pos += item->getNominalWidth(); + rect.mRight = pos; + item->setRect( rect ); + item->buildDrawLabel(); + } + } + reshape(rect.mRight, rect.getHeight()); +} + + +S32 LLMenuBarGL::getRightmostMenuEdge() +{ + // Find the last visible menu + item_list_t::reverse_iterator item_iter; + for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter) + { + if ((*item_iter)->getVisible()) + { + break; + } + } + + if (item_iter == mItems.rend()) + { + return 0; + } + return (*item_iter)->getRect().mRight; +} + +// add a vertical separator to this menu +bool LLMenuBarGL::addSeparator() +{ + LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL(); + return append( separator ); +} + +// add a menu - this will create a drop down menu. +bool LLMenuBarGL::appendMenu( LLMenuGL* menu ) +{ + if( menu == this ) + { + LL_ERRS() << "** Attempt to attach menu to itself. This is certainly " + << "a logic error." << LL_ENDL; + } + + bool success = true; + + // *TODO: Hack! Fix this + LLMenuItemBranchDownGL::Params p; + p.name = menu->getName(); + p.label = menu->getLabel(); + p.visible = menu->getVisible(); + p.branch = menu; + p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); + p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); + p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); + p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); + p.font = menu->getFont(); + + LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create(p); + success &= branch->addToAcceleratorList(&mAccelerators); + success &= append( branch ); + branch->setJumpKey(branch->getJumpKey()); + menu->updateParent(LLMenuGL::sMenuContainer); + + return success; +} + +bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) +{ + bool handled = false; + LLView* active_menu = NULL; + + bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; + S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; + S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; + mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2); + mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2); + mLastMouseX = x; + mLastMouseY = y; + + // if nothing currently selected or mouse has moved since last call, pick menu item via mouse + // otherwise let keyboard control it + if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0) + { + // find current active menu + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + if (((LLMenuItemGL*)viewp)->isOpen()) + { + active_menu = viewp; + } + } + + // check for new active menu + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if( viewp->getVisible() && + viewp->getEnabled() && + viewp->pointInView(local_x, local_y) && + viewp->handleHover(local_x, local_y, mask)) + { + ((LLMenuItemGL*)viewp)->setHighlight(true); + handled = true; + if (active_menu && active_menu != viewp) + { + ((LLMenuItemGL*)viewp)->onCommit(); + LLMenuGL::setKeyboardMode(false); + } + LLMenuGL::setKeyboardMode(false); + } + } + + if (handled) + { + // set hover false on inactive menus + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) + { + ((LLMenuItemGL*)viewp)->setHighlight(false); + } + } + } + } + + getWindow()->setCursor(UI_CURSOR_ARROW); + + return true; +} + +///============================================================================ +/// Class LLMenuHolderGL +///============================================================================ +LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX); + +LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p) + : LLPanel(p) +{ + sItemActivationTimer.stop(); + mCanHide = true; +} + +void LLMenuHolderGL::draw() +{ + LLView::draw(); + // now draw last selected item as overlay + LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get(); + 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(); + + LLRect item_rect; + selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this); + + F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME; + + LLUI::pushMatrix(); + { + LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom); + selecteditem->getMenu()->drawBackground(selecteditem, interpolant); + selecteditem->draw(); + } + LLUI::popMatrix(); + } +} + +bool LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + if (!handled) + { + LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu(); + LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL; + if (parent_menu && parent_menu->getVisible()) + { + // don't hide menu if parent was hit + LLRect parent_rect; + parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this); + if (!parent_rect.pointInRect(x, y)) + { + // clicked off of menu and parent, hide them all + hideMenus(); + } + } + else + { + // no visible parent, clicked off of menu, hide them all + hideMenus(); + } + } + return handled; +} + +bool LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) +{ + bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL; + if (!handled) + { + // clicked off of menu, hide them all + hideMenus(); + } + 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 if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN)) + { + //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()) + { + hideMenus(); + } + LLView::reshape(width, height, called_from_parent); +} + +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) + { + return viewp; + } + } + return NULL; +} + + +bool LLMenuHolderGL::hideMenus() +{ + if (!mCanHide) + { + return false; + } + LLMenuGL::setKeyboardMode(false); + bool menu_visible = hasVisibleMenu(); + if (menu_visible) + { + // clicked off of menu, hide them all + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLView* viewp = *child_it; + if (dynamic_cast(viewp) != NULL && viewp->getVisible()) + { + viewp->setVisible(false); + } + } + } + //if (gFocusMgr.childHasKeyboardFocus(this)) + //{ + // gFocusMgr.setKeyboardFocus(NULL); + //} + + return menu_visible; +} + +void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item) +{ + sItemLastSelectedHandle = item->getHandle(); + sItemActivationTimer.start(); +} + +///============================================================================ +/// Class LLTearOffMenu +///============================================================================ +LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : + LLFloater(LLSD()), + mQuitRequested(false) +{ + S32 floater_header_size = getHeaderHeight(); + + 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->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 = rect.getHeight() + floater_header_size; + reshape(rect.getWidth(), rect.getHeight()); + setRect(rect); + + // attach menu to floater + menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); + mOldParent = menup->getParent(); + addChild(menup); + menup->setVisible(true); + 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; + + // highlight first item (tear off item will be disabled) + mMenu->highlightNextItem(NULL); + + // Can't do this in postBuild() because that is only called for floaters + // constructed from XML. + mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this)); +} + +LLTearOffMenu::~LLTearOffMenu() +{ +} + +void LLTearOffMenu::draw() +{ + mMenu->setBackgroundVisible(isBackgroundOpaque()); + + if (getRect().getHeight() != mTargetHeight) + { + // animate towards target height + reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f)))); + } + mMenu->needsArrange(); + LLFloater::draw(); +} + +void LLTearOffMenu::onFocusReceived() +{ + if (mQuitRequested) + { + return; + } + + // if nothing is highlighted, just highlight first item + if (!mMenu->getHighlightedItem()) + { + mMenu->highlightNextItem(NULL); + } + + // parent menu items get highlights so navigation logic keeps working + LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem(); + while(parent_menu_item) + { + if (parent_menu_item->getMenu()->getVisible()) + { + parent_menu_item->setHighlight(true); + parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem(); + } + else + { + break; + } + } + LLFloater::onFocusReceived(); +} + +void LLTearOffMenu::onFocusLost() +{ + // remove highlight from parent item and our own menu + mMenu->clearHoverItem(); + LLFloater::onFocusLost(); +} + +bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent) +{ + // pass keystrokes down to menu + return mMenu->handleUnicodeChar(uni_char, true); +} + +bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask) +{ + if (!mMenu->getHighlightedItem()) + { + if (key == KEY_UP) + { + mMenu->highlightPrevItem(NULL); + return true; + } + else if (key == KEY_DOWN) + { + mMenu->highlightNextItem(NULL); + return true; + } + } + // pass keystrokes down to menu + return mMenu->handleKey(key, mask, true); +} + +void LLTearOffMenu::translate(S32 x, S32 y) +{ + if (x != 0 && y != 0) + { + // hide open sub-menus by clearing current hover item + mMenu->clearHoverItem(); + } + LLFloater::translate(x, y); +} + +//static +LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup) +{ + LLTearOffMenu* tearoffp = new LLTearOffMenu(menup); + // keep onscreen + gFloaterView->adjustToFitScreen(tearoffp, false); + tearoffp->openFloater(LLSD()); + + return tearoffp; +} + +void LLTearOffMenu::updateSize() +{ + if (mMenu) + { + S32 floater_header_size = getHeaderHeight(); + const LLRect &floater_rect = getRect(); + LLRect new_rect; + mMenu->localRectToOtherView(LLRect(-1, mMenu->getRect().getHeight() + floater_header_size, mMenu->getRect().getWidth() + 3, 0), &new_rect, gFloaterView); + + if (floater_rect.getWidth() != new_rect.getWidth() + || mTargetHeight != new_rect.getHeight()) + { + // make sure this floater is big enough for menu + mTargetHeight = new_rect.getHeight(); + reshape(new_rect.getWidth(), mTargetHeight); + + // Restore menu position + LLRect menu_rect = mMenu->getRect(); + menu_rect.setOriginAndSize(1, 1, + menu_rect.getWidth(), menu_rect.getHeight()); + mMenu->setRect(menu_rect); + } + } +} + +void LLTearOffMenu::closeTearOff() +{ + removeChild(mMenu); + mOldParent->addChild(mMenu); + mMenu->clearHoverItem(); + mMenu->setFollowsNone(); + mMenu->setBackgroundVisible(true); + mMenu->setVisible(false); + mMenu->setTornOff(false); + mMenu->setDropShadowed(true); + mQuitRequested = true; +} + +LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p) +: LLMenuItemGL(p) +{ + LLContextMenu* branch = static_cast(p.branch); + if (branch) + { + mBranch = branch->getHandle(); + branch->hide(); + branch->setParentMenuItem(this); + } +} + +LLContextMenuBranch::~LLContextMenuBranch() +{ + if (mBranch.get()) + { + mBranch.get()->die(); + } +} + +// called to rebuild the draw label +void LLContextMenuBranch::buildDrawLabel( void ) +{ + auto menu = getBranch(); + if (menu) + { + // default enablement is this -- if any of the subitems are + // enabled, this item is enabled. JC + U32 sub_count = menu->getItemCount(); + U32 i; + bool any_enabled = false; + for (i = 0; i < sub_count; i++) + { + LLMenuItemGL* item = menu->getItem(i); + item->buildDrawLabel(); + if (item->getEnabled() && !item->getDrawTextDisabled() ) + { + any_enabled = true; + break; + } + } + setDrawTextDisabled(!any_enabled); + setEnabled(true); + } + + mDrawAccelLabel.clear(); + std::string st = mDrawAccelLabel; + appendAcceleratorString( st ); + mDrawAccelLabel = st; + + mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; +} + +void LLContextMenuBranch::showSubMenu() +{ + auto menu = getBranch(); + if(menu) + { + LLMenuItemGL* menu_item = menu->getParentMenuItem(); + if (menu_item != NULL && menu_item->getVisible()) + { + S32 center_x; + S32 center_y; + localPointToScreen(getRect().getWidth(), getRect().getHeight(), ¢er_x, ¢er_y); + menu->show(center_x, center_y); + } + } +} + +// onCommit() - do the primary funcationality of the menu item. +void LLContextMenuBranch::onCommit( void ) +{ + showSubMenu(); + +} +void LLContextMenuBranch::setHighlight( bool highlight ) +{ + if (highlight == getHighlight()) return; + LLMenuItemGL::setHighlight(highlight); + auto menu = getBranch(); + if (menu) + { + if (highlight) + { + showSubMenu(); + } + else + { + menu->hide(); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// class LLContextMenu +// A context menu +//----------------------------------------------------------------------------- +static LLDefaultChildRegistry::Register context_menu_register("context_menu"); +static MenuRegistry::Register context_menu_register2("context_menu"); + + +LLContextMenu::LLContextMenu(const Params& p) +: LLMenuGL(p), + mHoveredAnyItem(false), + mHoverItem(NULL) +{ + //setBackgroundVisible(true); +} + +void LLContextMenu::setVisible(bool visible) +{ + if (!visible) + hide(); +} + +// Takes cursor position in screen space? +void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view) +{ + if (getChildList()->empty()) + { + // nothing to show, so abort + 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 LLMenuGL::showPopup() + LLMenuHolderGL::sContextMenuSpawnPos.set(x,y); + + arrangeAndClear(); + + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); + const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); + LLView* parent_view = getParent(); + + // Open upwards if menu extends past bottom + if (y - height < menu_region_rect.mBottom) + { + if (getParentMenuItem()) // Adjust if this is a submenu + { + y += height - getParentMenuItem()->getNominalHeight(); + } + else + { + y += height; + } + } + + // Open out to the left if menu extends past right edge + if (x + width > menu_region_rect.mRight) + { + if (getParentMenuItem()) + { + x -= getParentMenuItem()->getRect().getWidth() + width; + } + else + { + x -= width; + } + } + + S32 local_x, local_y; + parent_view->screenPointToLocal(x, y, &local_x, &local_y); + + LLRect rect; + rect.setLeftTopAndSize(local_x, local_y, width, height); + setRect(rect); + arrange(); + + if (spawning_view) + { + mSpawningViewHandle = spawning_view->getHandle(); + } + else + { + mSpawningViewHandle.markDead(); + } + LLView::setVisible(true); +} + +void LLContextMenu::hide() +{ + if (!getVisible()) return; + + LLView::setVisible(false); + + if (mHoverItem) + { + mHoverItem->setHighlight( false ); + } + mHoverItem = NULL; +} + + +bool LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) +{ + LLMenuGL::handleHover(x,y,mask); + + bool handled = false; + + LLMenuItemGL *item = getHighlightedItem(); + + if (item && item->getEnabled()) + { + getWindow()->setCursor(UI_CURSOR_ARROW); + handled = true; + + if (item != mHoverItem) + { + if (mHoverItem) + { + mHoverItem->setHighlight( false ); + } + mHoverItem = item; + mHoverItem->setHighlight( true ); + } + mHoveredAnyItem = true; + } + else + { + // clear out our selection + if (mHoverItem) + { + mHoverItem->setHighlight(false); + mHoverItem = NULL; + } + } + + if( !handled && pointInView( x, y ) ) + { + getWindow()->setCursor(UI_CURSOR_ARROW); + handled = true; + } + + return handled; +} + +// handleMouseDown and handleMouseUp are handled by LLMenuGL + + +bool LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // The click was somewhere within our rectangle + LLMenuItemGL *item = getHighlightedItem(); + + S32 local_x = x - getRect().mLeft; + S32 local_y = y - getRect().mBottom; + + bool clicked_in_menu = pointInView(local_x, local_y) ; + + // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie + if (clicked_in_menu) + { + // capture mouse cursor as if on initial menu show + handled = true; + } + + if (item) + { + // lie to the item about where the click happened + // to make sure it's within the item's rectangle + if (item->handleMouseDown( 0, 0, mask )) + { + handled = true; + } + } + + return handled; +} + +bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) +{ + S32 local_x = x - getRect().mLeft; + S32 local_y = y - getRect().mBottom; + + if (!mHoveredAnyItem && !pointInView(local_x, local_y)) + { + sMenuContainer->hideMenus(); + return true; + } + + + bool result = handleMouseUp( x, y, mask ); + mHoveredAnyItem = false; + + return result; +} + +bool LLContextMenu::addChild(LLView* view, S32 tab_group) +{ + return addContextChild(view, tab_group); +} + diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 9b22a6e0f2..51766afe85 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -1,978 +1,978 @@ -/** - * @file llmenugl.h - * @brief Declaration of the opengl based menu system. - * - * $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$ - */ - -#ifndef LL_LLMENUGL_H -#define LL_LLMENUGL_H - -#include - -#include "llstring.h" -#include "v4color.h" -#include "llframetimer.h" - -#include "llkeyboard.h" -#include "llfloater.h" -#include "lluistring.h" -#include "llview.h" -#include - -extern S32 MENU_BAR_HEIGHT; -extern S32 MENU_BAR_WIDTH; - -class LLMenuKeyboardBinding -{ -public: - KEY mKey; - MASK mMask; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemGL -// -// The LLMenuItemGL represents a single menu item in a menu. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemGL: public LLUICtrl, public ll::ui::SearchableControl -{ -public: - struct Params : public LLInitParam::Block - { - Optional shortcut; - Optional jump_key; - Optional use_mac_ctrl, - allow_key_repeat; - - Ignored rect, - left, - top, - right, - bottom, - width, - height, - bottom_delta, - left_delta; - - Optional enabled_color, - disabled_color, - highlight_bg_color, - highlight_fg_color; - - - Params(); - }; - -protected: - LLMenuItemGL(const Params&); - friend class LLUICtrlFactory; -public: - // LLView overrides - /*virtual*/ void onVisibilityChange(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 hasAccelerator(const KEY &key, const MASK &mask) const; - virtual bool handleAcceleratorKey(KEY key, MASK mask); - - LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); } - - void setJumpKey(KEY key); - KEY getJumpKey() const { return mJumpKey; } - - // set the font used by this item. - void setFont(const LLFontGL* font) { mFont = font; } - const LLFontGL* getFont() const { return mFont; } - - // returns the height in pixels for the current font. - virtual U32 getNominalHeight( void ) const; - - // Marks item as not needing space for check marks or accelerator keys - virtual void setBriefItem(bool brief); - virtual bool isBriefItem() const; - - virtual bool addToAcceleratorList(std::list *listp); - void setAllowKeyRepeat(bool allow) { mAllowKeyRepeat = allow; } - bool getAllowKeyRepeat() const { return mAllowKeyRepeat; } - - // change the label - void setLabel( const LLStringExplicit& label ) { mLabel = label; } - std::string getLabel( void ) const { return mLabel.getString(); } - virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - - // Get the parent menu for this item - 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 - // arrangement. - virtual U32 getNominalWidth( void ) const; - - // buildDrawLabel() - constructs the string used during the draw() - // function. This reduces the overall string manipulation, but can - // lead to visual errors if the state of the object changes - // without the knowledge of the menu item. For example, if a - // boolean being watched is changed outside of the menu item's - // onCommit() function, the draw buffer will not be updated and will - // reflect the wrong value. If this ever becomes an issue, there - // are ways to fix this. - // Returns the enabled state of the item. - virtual void buildDrawLabel( void ); - - // for branching menu items, bring sub menus up to root level of menu hierarchy - virtual void updateBranchParent( LLView* parentp ){}; - - virtual void onCommit( void ); - - virtual void setHighlight( bool highlight ); - virtual bool getHighlight() const { return mHighlight; } - - // determine if this represents an active sub-menu - virtual bool isActive( void ) const { return false; } - - // determine if this represents an open sub-menu - virtual bool isOpen( void ) const { return false; } - - virtual void setEnabledSubMenus(bool enable){}; - - // LLView Functionality - 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 onMouseEnter(S32 x, S32 y, MASK mask); - virtual void onMouseLeave(S32 x, S32 y, MASK mask); - - virtual void draw( void ); - - bool getHover() const { return mGotHover; } - - void setDrawTextDisabled(bool disabled) { mDrawTextDisabled = disabled; } - bool getDrawTextDisabled() const { return mDrawTextDisabled; } - -protected: - void setHover(bool hover) { mGotHover = hover; } - - // This function appends the character string representation of - // the current accelerator key and mask to the provided string. - void appendAcceleratorString( std::string& st ) const; - - virtual std::string _getSearchText() const - { - return mLabel.getString(); - } - -protected: - KEY mAcceleratorKey; - MASK mAcceleratorMask; - // mLabel contains the actual label specified by the user. - LLUIString mLabel; - - // The draw labels contain some of the labels that we draw during - // the draw() routine. This optimizes away some of the string - // manipulation. - LLUIString mDrawBoolLabel; - LLUIString mDrawAccelLabel; - LLUIString mDrawBranchLabel; - - LLUIColor mEnabledColor; - LLUIColor mDisabledColor; - LLUIColor mHighlightBackground; - LLUIColor mHighlightForeground; - - bool mHighlight; -private: - // Keyboard and mouse variables - bool mAllowKeyRepeat; - bool mGotHover; - - // If true, suppress normal space for check marks on the left and accelerator - // keys on the right. - bool mBriefItem; - - // Font for this item - const LLFontGL* mFont; - bool mDrawTextDisabled; - - KEY mJumpKey; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemSeparatorGL -// -// This class represents a separator. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLMenuItemSeparatorGL : public LLMenuItemGL -{ -public: - struct Params : public LLInitParam::Block - { - Optional on_visible; - Params(); - }; - - LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params()); - - /*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 void buildDrawLabel(); - - /*virtual*/ U32 getNominalHeight( void ) const; - -private: - enable_signal_t mVisibleSignal; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemCallGL -// -// The LLMenuItemCallerGL represents a single menu item in a menu that -// calls a user defined callback. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemCallGL : public LLMenuItemGL -{ -public: - struct Params : public LLInitParam::Block - { - Optional on_enable; - Optional on_click; - Optional on_visible; - Params() - : on_enable("on_enable"), - on_click("on_click"), - on_visible("on_visible") - {} - }; -protected: - LLMenuItemCallGL(const Params&); - friend class LLUICtrlFactory; - void updateEnabled( void ); - void updateVisible( void ); - -public: - void initFromParams(const Params& p); - - // called to rebuild the draw label - virtual void buildDrawLabel( void ); - - virtual void onCommit( void ); - - virtual bool handleAcceleratorKey(KEY key, MASK mask); - virtual bool handleKeyHere(KEY key, MASK mask); - - //virtual void draw(); - - boost::signals2::connection setClickCallback( const commit_signal_t::slot_type& cb ) - { - return setCommitCallback(cb); - } - - boost::signals2::connection setEnableCallback( const enable_signal_t::slot_type& cb ) - { - return mEnableSignal.connect(cb); - } - -private: - enable_signal_t mEnableSignal; - enable_signal_t mVisibleSignal; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemCheckGL -// -// The LLMenuItemCheckGL is an extension of the LLMenuItemCallGL -// class, by allowing another method to be specified which determines -// if the menu item should consider itself checked as true or not. Be -// careful that the provided callback is fast - it needs to be VERY -// EFFICIENT because it may need to be checked a lot. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemCheckGL -: public LLMenuItemCallGL -{ -public: - struct Params : public LLInitParam::Block - { - Optional on_check; - Params() - : on_check("on_check") - {} - }; - -protected: - LLMenuItemCheckGL(const Params&); - friend class LLUICtrlFactory; -public: - - void initFromParams(const Params& p); - - virtual void onCommit( void ); - - virtual void setValue(const LLSD& value); - virtual LLSD getValue() const; - - // called to rebuild the draw label - virtual void buildDrawLabel( void ); - - boost::signals2::connection setCheckCallback( const enable_signal_t::slot_type& cb ) - { - return mCheckSignal.connect(cb); - } - -private: - enable_signal_t mCheckSignal; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuGL -// -// The Menu class represents a normal rectangular menu somewhere on -// screen. A Menu can have menu items (described above) or sub-menus -// attached to it. Sub-menus are implemented via a specialized -// menu-item type known as a branch. Since it's easy to do wrong, I've -// taken the branch functionality out of public view, and encapsulate -// it in the appendMenu() method. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// child widget registry -struct MenuRegistry : public LLChildRegistry -{ - LLSINGLETON_EMPTY_CTOR(MenuRegistry); -}; - - -class LLMenuGL -: public LLUICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Optional jump_key; - Optional horizontal_layout, - can_tear_off, - drop_shadow, - bg_visible, - create_jump_keys, - keep_fixed_size, - scrollable; - Optional max_scrollable_items; - Optional preferred_width; - Optional bg_color; - Optional shortcut_pad; - - Params() - : jump_key("jump_key", KEY_NONE), - horizontal_layout("horizontal_layout"), - can_tear_off("tear_off", false), - drop_shadow("drop_shadow", true), - bg_visible("bg_visible", true), - create_jump_keys("create_jump_keys", false), - keep_fixed_size("keep_fixed_size", false), - bg_color("bg_color", LLUIColorTable::instance().getColor( "MenuDefaultBgColor" )), - scrollable("scrollable", false), - max_scrollable_items("max_scrollable_items", U32_MAX), - preferred_width("preferred_width", U32_MAX), - shortcut_pad("shortcut_pad") - { - addSynonym(bg_visible, "opaque"); - addSynonym(bg_color, "color"); - addSynonym(can_tear_off, "can_tear_off"); - } - }; - - // my valid children are contained in MenuRegistry - typedef MenuRegistry child_registry_t; - - void initFromParams(const Params&); - - // 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; - - // for scrollable menus - typedef enum e_scrolling_direction - { - SD_UP = 0, - SD_DOWN = 1, - SD_BEGIN = 2, - SD_END = 3 - } EScrollingDirection; - -protected: - LLMenuGL(const LLMenuGL::Params& p); - friend class LLUICtrlFactory; - // let branching menu items use my protected traversal methods - friend class LLMenuItemBranchGL; -public: - virtual ~LLMenuGL( void ); - - void parseChildXML(LLXMLNodePtr child, LLView* parent); - - // LLView Functionality - /*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, F32 alpha); - /*virtual*/ void setVisible(bool visible); - /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); - /*virtual*/ void deleteAllChildren(); - /*virtual*/ void removeChild( LLView* ctrl); - /*virtual*/ bool postBuild(); - - virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; - virtual bool handleAcceleratorKey(KEY key, MASK mask); - - LLMenuGL* findChildMenuByName(const std::string& name, bool recurse) const; - - bool clearHoverItem(); - - // return the name label - const std::string& getLabel( void ) const { return mLabel.getString(); } - void setLabel(const LLStringExplicit& label) { mLabel = label; } - - // background colors - void setBackgroundColor( const LLUIColor& color ) { mBackgroundColor = color; } - const LLUIColor& getBackgroundColor() const { return mBackgroundColor; } - void setBackgroundVisible( bool b ) { mBgVisible = b; } - void setCanTearOff(bool tear_off); - - // add a separator to this menu - virtual bool addSeparator(); - - // for branching menu items, bring sub menus up to root level of menu hierarchy - virtual void updateParent( LLView* parentp ); - - // setItemEnabled() - pass the name and the enable flag for a - // menu item. true will make sure it's enabled, false will disable - // it. - void setItemEnabled( const std::string& name, bool enable ); - - // propagate message to submenus - void setEnabledSubMenus(bool enable); - - void setItemVisible( const std::string& name, bool visible); - - void setItemLabel(const std::string &name, const std::string &label); - - // sets the left,bottom corner of menu, useful for popups - void setLeftAndBottom(S32 left, S32 bottom); - - virtual bool handleJumpKey(KEY key); - - virtual bool jumpKeysActive(); - - 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 ); - - // erase group of items from menu - void erase( S32 begin, S32 end, bool arrange = true ); - - // add new item at position - void insert( S32 begin, LLView * ctrl, bool arrange = true ); - - void setItemLastSelected(LLMenuItemGL* item); // must be in menu - U32 getItemCount(); // number of menu items - LLMenuItemGL* getItem(S32 number); // 0 = first item - LLMenuItemGL* getItem(std::string name); - LLMenuItemGL* getHighlightedItem(); - - LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled = true); - LLMenuItemGL* highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled = true); - - void buildDrawLabels(); - void createJumpKeys(); - - // Show popup at a specific location, in the spawn_view's coordinate frame - static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x = 0, S32 mouse_y = 0); - - // Whether to drop shadow menu bar - void setDropShadowed( const bool shadowed ); - - 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; } - - bool getCanTearOff() { return mTearOffItem != NULL; } - - KEY getJumpKey() const { return mJumpKey; } - void setJumpKey(KEY key) { mJumpKey = key; } - - static void setKeyboardMode(bool mode) { sKeyboardMode = mode; } - static bool getKeyboardMode() { return sKeyboardMode; } - - S32 getShortcutPad() { return mShortcutPad; } - - bool scrollItems(EScrollingDirection direction); - bool isScrollable() const { return mScrollable; } - - static class LLMenuHolderGL* sMenuContainer; - - void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; } - bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; } - - void setAlwaysShowMenu(bool show) { mAlwaysShowMenu = show; } - bool getAlwaysShowMenu() { return mAlwaysShowMenu; } - - // add a context menu branch - bool appendContextSubMenu(LLMenuGL *menu); - - const LLFontGL *getFont() const { return mFont; } - - 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 ); - - // Used in LLContextMenu and in LLTogleableMenu - // to add an item of context menu branch - bool addContextChild(LLView* view, S32 tab_group); - - // TODO: create accessor methods for these? - typedef std::list< LLMenuItemGL* > item_list_t; - item_list_t mItems; - LLMenuItemGL*mFirstVisibleItem; - LLMenuItemGL *mArrowUpItem, *mArrowDownItem; - - typedef std::map navigation_key_map_t; - navigation_key_map_t mJumpKeys; - S32 mLastMouseX; - S32 mLastMouseY; - S32 mMouseVelX; - S32 mMouseVelY; - U32 mMaxScrollableItems; - U32 mPreferredWidth; - bool mHorizontalLayout; - bool mScrollable; - bool mKeepFixedSize; - bool mNeedsArrange; - - // Font for top menu items only - const LLFontGL* mFont; - -private: - - - static LLColor4 sDefaultBackgroundColor; - static bool sKeyboardMode; - - bool mAlwaysShowMenu; - - LLUIColor mBackgroundColor; - bool mBgVisible; - LLHandle mParentMenuItem; - LLUIString mLabel; - bool mDropShadowed; // Whether to drop shadow - bool mHasSelection; - LLFrameTimer mFadeTimer; - LLTimer mScrollItemsTimer; - bool mTornOff; - class LLMenuItemTearOffGL* mTearOffItem; - class LLMenuItemBranchGL* mSpilloverBranch; - LLMenuGL* mSpilloverMenu; - KEY mJumpKey; - bool mCreateJumpKeys; - S32 mShortcutPad; - bool mResetScrollPositionOnShow; -}; // end class LLMenuGL - - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemBranchGL -// -// The LLMenuItemBranchGL represents a menu item that has a -// sub-menu. This is used to make cascading menus. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemBranchGL : public LLMenuItemGL -{ -public: - struct Params : public LLInitParam::Block - { - Optional branch; - }; - -protected: - LLMenuItemBranchGL(const Params&); - friend class LLUICtrlFactory; -public: - virtual ~LLMenuItemBranchGL(); - - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - - virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; - virtual bool handleAcceleratorKey(KEY key, MASK mask); - - // check if we've used these accelerators already - virtual bool addToAcceleratorList(std::list *listp); - - // called to rebuild the draw label - virtual void buildDrawLabel( void ); - - virtual void onCommit( void ); - - virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); - virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); - - // set the hover status (called by it's menu) and if the object is - // active. This is used for behavior transfer. - virtual void setHighlight( bool highlight ); - - virtual bool handleKeyHere(KEY key, MASK mask); - - virtual bool isActive() const; - - virtual bool isOpen() const; - - LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); } - - virtual void updateBranchParent( LLView* parentp ); - - // LLView Functionality - virtual void onVisibilityChange( bool curVisibilityIn ); - - virtual void draw(); - - virtual void setEnabledSubMenus(bool enabled) { if (getBranch()) getBranch()->setEnabledSubMenus(enabled); } - - virtual void openMenu(); - - virtual LLView* getChildView(const std::string& name, bool recurse = true) const; - virtual LLView* findChildView(const std::string& name, bool recurse = true) const; - -private: - LLHandle mBranchHandle; -}; // end class LLMenuItemBranchGL - - -//----------------------------------------------------------------------------- -// class LLContextMenu -// A context menu -//----------------------------------------------------------------------------- - -class LLContextMenu -: public LLMenuGL -{ -public: - struct Params : public LLInitParam::Block - { - Params() - { - changeDefault(visible, false); - } - }; - -protected: - LLContextMenu(const Params& p); - friend class LLUICtrlFactory; - -public: - virtual ~LLContextMenu() {} - - // LLView Functionality - // can't set visibility directly, must call show or hide - virtual void setVisible (bool visible); - - virtual void show (S32 x, S32 y, LLView* spawning_view = NULL); - virtual void hide (); - - 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 addChild (LLView* view, S32 tab_group = 0); - - LLHandle getHandle() { return getDerivedHandle(); } - - LLView* getSpawningView() const { return mSpawningViewHandle.get(); } - void setSpawningView(LLHandle spawning_view) { mSpawningViewHandle = spawning_view; } - -protected: - bool mHoveredAnyItem; - LLMenuItemGL* mHoverItem; - LLRootHandle mHandle; - LLHandle mSpawningViewHandle; -}; - -//----------------------------------------------------------------------------- -// class LLContextMenuBranch -// A branch to another context menu -//----------------------------------------------------------------------------- -class LLContextMenuBranch : public LLMenuItemGL -{ -public: - struct Params : public LLInitParam::Block - { - Mandatory branch; - }; - - LLContextMenuBranch(const Params&); - - virtual ~LLContextMenuBranch(); - - // called to rebuild the draw label - virtual void buildDrawLabel( void ); - - // onCommit() - do the primary funcationality of the menu item. - virtual void onCommit( void ); - - LLContextMenu* getBranch() { return mBranch.get(); } - void setHighlight( bool highlight ); - -protected: - void showSubMenu(); - - LLHandle mBranch; -}; - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuBarGL -// -// A menu bar displays menus horizontally. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuBarGL : public LLMenuGL -{ -public: - struct Params : public LLInitParam::Block - {}; - LLMenuBarGL( const Params& p ); - virtual ~LLMenuBarGL(); - - /*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 handleDoubleClick(S32 x, S32 y, MASK mask); - - /*virtual*/ void draw(); - /*virtual*/ bool jumpKeysActive(); - - // add a vertical separator to this menu - virtual bool addSeparator(); - - // LLView Functionality - virtual bool handleHover( S32 x, S32 y, MASK mask ); - - // Returns x position of rightmost child, usually Help menu - S32 getRightmostMenuEdge(); - - 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; - bool mAltKeyTrigger; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuHolderGL -// -// High level view that serves as parent for all menus -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLMenuHolderGL : public LLPanel -{ -public: - struct Params : public LLInitParam::Block - {}; - LLMenuHolderGL(const Params& p); - virtual ~LLMenuHolderGL() {} - - virtual bool hideMenus(); - void reshape(S32 width, S32 height, bool called_from_parent = true); - void setCanHide(bool can_hide) { mCanHide = can_hide; } - - // LLView functionality - virtual void draw(); - 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(); } - 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; - - bool mCanHide; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLTearOffMenu -// -// Floater that hosts a menu -// https://wiki.lindenlab.com/mediawiki/index.php?title=LLTearOffMenu&oldid=81344 -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLTearOffMenu : public LLFloater -{ -public: - static LLTearOffMenu* create(LLMenuGL* menup); - virtual ~LLTearOffMenu(); - - virtual void draw(void); - virtual void onFocusReceived(); - virtual void onFocusLost(); - virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); - virtual bool handleKeyHere(KEY key, MASK mask); - virtual void translate(S32 x, S32 y); - - void updateSize(); - -private: - LLTearOffMenu(LLMenuGL* menup); - - void closeTearOff(); - - LLView* mOldParent; - LLMenuGL* mMenu; - S32 mTargetHeight; - bool mQuitRequested; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemTearOffGL -// -// This class represents a separator. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemTearOffGL : public LLMenuItemGL -{ -public: - struct Params : public LLInitParam::Block - {}; - - LLMenuItemTearOffGL( const Params& ); - - virtual void onCommit(void); - virtual void draw(void); - virtual U32 getNominalHeight() const; - - LLFloater* getParentFloater(); -}; - - -// *TODO: this is currently working, so finish implementation -class LLEditMenuHandlerMgr -{ -public: - LLEditMenuHandlerMgr& getInstance() { - static LLEditMenuHandlerMgr instance; - return instance; - } - virtual ~LLEditMenuHandlerMgr() {} -private: - LLEditMenuHandlerMgr() {}; -}; - - -// *TODO: Eliminate -// For backwards compatability only; generally just use boost::bind -class view_listener_t : public boost::signals2::trackable -{ -public: - virtual bool handleEvent(const LLSD& userdata) = 0; - view_listener_t() { sListeners.insert(this); } - virtual ~view_listener_t() { sListeners.erase(this); } - - static void addEnable(view_listener_t* listener, const std::string& name) - { - LLUICtrl::EnableCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2)); - } - - static void addCommit(view_listener_t* listener, const std::string& name) - { - LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2)); - } - - static void addMenu(view_listener_t* listener, const std::string& name) - { - // For now, add to both click and enable registries - addEnable(listener, name); - addCommit(listener, name); - } - - static void cleanup() - { - listener_vector_t listeners(sListeners.begin(), sListeners.end()); - sListeners.clear(); - - std::for_each(listeners.begin(), listeners.end(), DeletePointer()); - listeners.clear(); - } - -private: - typedef std::set listener_map_t; - typedef std::vector listener_vector_t; - static listener_map_t sListeners; -}; - -#endif // LL_LLMENUGL_H +/** + * @file llmenugl.h + * @brief Declaration of the opengl based menu system. + * + * $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$ + */ + +#ifndef LL_LLMENUGL_H +#define LL_LLMENUGL_H + +#include + +#include "llstring.h" +#include "v4color.h" +#include "llframetimer.h" + +#include "llkeyboard.h" +#include "llfloater.h" +#include "lluistring.h" +#include "llview.h" +#include + +extern S32 MENU_BAR_HEIGHT; +extern S32 MENU_BAR_WIDTH; + +class LLMenuKeyboardBinding +{ +public: + KEY mKey; + MASK mMask; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemGL +// +// The LLMenuItemGL represents a single menu item in a menu. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuItemGL: public LLUICtrl, public ll::ui::SearchableControl +{ +public: + struct Params : public LLInitParam::Block + { + Optional shortcut; + Optional jump_key; + Optional use_mac_ctrl, + allow_key_repeat; + + Ignored rect, + left, + top, + right, + bottom, + width, + height, + bottom_delta, + left_delta; + + Optional enabled_color, + disabled_color, + highlight_bg_color, + highlight_fg_color; + + + Params(); + }; + +protected: + LLMenuItemGL(const Params&); + friend class LLUICtrlFactory; +public: + // LLView overrides + /*virtual*/ void onVisibilityChange(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 hasAccelerator(const KEY &key, const MASK &mask) const; + virtual bool handleAcceleratorKey(KEY key, MASK mask); + + LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); } + + void setJumpKey(KEY key); + KEY getJumpKey() const { return mJumpKey; } + + // set the font used by this item. + void setFont(const LLFontGL* font) { mFont = font; } + const LLFontGL* getFont() const { return mFont; } + + // returns the height in pixels for the current font. + virtual U32 getNominalHeight( void ) const; + + // Marks item as not needing space for check marks or accelerator keys + virtual void setBriefItem(bool brief); + virtual bool isBriefItem() const; + + virtual bool addToAcceleratorList(std::list *listp); + void setAllowKeyRepeat(bool allow) { mAllowKeyRepeat = allow; } + bool getAllowKeyRepeat() const { return mAllowKeyRepeat; } + + // change the label + void setLabel( const LLStringExplicit& label ) { mLabel = label; } + std::string getLabel( void ) const { return mLabel.getString(); } + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); + + // Get the parent menu for this item + 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 + // arrangement. + virtual U32 getNominalWidth( void ) const; + + // buildDrawLabel() - constructs the string used during the draw() + // function. This reduces the overall string manipulation, but can + // lead to visual errors if the state of the object changes + // without the knowledge of the menu item. For example, if a + // boolean being watched is changed outside of the menu item's + // onCommit() function, the draw buffer will not be updated and will + // reflect the wrong value. If this ever becomes an issue, there + // are ways to fix this. + // Returns the enabled state of the item. + virtual void buildDrawLabel( void ); + + // for branching menu items, bring sub menus up to root level of menu hierarchy + virtual void updateBranchParent( LLView* parentp ){}; + + virtual void onCommit( void ); + + virtual void setHighlight( bool highlight ); + virtual bool getHighlight() const { return mHighlight; } + + // determine if this represents an active sub-menu + virtual bool isActive( void ) const { return false; } + + // determine if this represents an open sub-menu + virtual bool isOpen( void ) const { return false; } + + virtual void setEnabledSubMenus(bool enable){}; + + // LLView Functionality + 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 onMouseEnter(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask); + + virtual void draw( void ); + + bool getHover() const { return mGotHover; } + + void setDrawTextDisabled(bool disabled) { mDrawTextDisabled = disabled; } + bool getDrawTextDisabled() const { return mDrawTextDisabled; } + +protected: + void setHover(bool hover) { mGotHover = hover; } + + // This function appends the character string representation of + // the current accelerator key and mask to the provided string. + void appendAcceleratorString( std::string& st ) const; + + virtual std::string _getSearchText() const + { + return mLabel.getString(); + } + +protected: + KEY mAcceleratorKey; + MASK mAcceleratorMask; + // mLabel contains the actual label specified by the user. + LLUIString mLabel; + + // The draw labels contain some of the labels that we draw during + // the draw() routine. This optimizes away some of the string + // manipulation. + LLUIString mDrawBoolLabel; + LLUIString mDrawAccelLabel; + LLUIString mDrawBranchLabel; + + LLUIColor mEnabledColor; + LLUIColor mDisabledColor; + LLUIColor mHighlightBackground; + LLUIColor mHighlightForeground; + + bool mHighlight; +private: + // Keyboard and mouse variables + bool mAllowKeyRepeat; + bool mGotHover; + + // If true, suppress normal space for check marks on the left and accelerator + // keys on the right. + bool mBriefItem; + + // Font for this item + const LLFontGL* mFont; + bool mDrawTextDisabled; + + KEY mJumpKey; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemSeparatorGL +// +// This class represents a separator. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLMenuItemSeparatorGL : public LLMenuItemGL +{ +public: + struct Params : public LLInitParam::Block + { + Optional on_visible; + Params(); + }; + + LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params()); + + /*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 void buildDrawLabel(); + + /*virtual*/ U32 getNominalHeight( void ) const; + +private: + enable_signal_t mVisibleSignal; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemCallGL +// +// The LLMenuItemCallerGL represents a single menu item in a menu that +// calls a user defined callback. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuItemCallGL : public LLMenuItemGL +{ +public: + struct Params : public LLInitParam::Block + { + Optional on_enable; + Optional on_click; + Optional on_visible; + Params() + : on_enable("on_enable"), + on_click("on_click"), + on_visible("on_visible") + {} + }; +protected: + LLMenuItemCallGL(const Params&); + friend class LLUICtrlFactory; + void updateEnabled( void ); + void updateVisible( void ); + +public: + void initFromParams(const Params& p); + + // called to rebuild the draw label + virtual void buildDrawLabel( void ); + + virtual void onCommit( void ); + + virtual bool handleAcceleratorKey(KEY key, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); + + //virtual void draw(); + + boost::signals2::connection setClickCallback( const commit_signal_t::slot_type& cb ) + { + return setCommitCallback(cb); + } + + boost::signals2::connection setEnableCallback( const enable_signal_t::slot_type& cb ) + { + return mEnableSignal.connect(cb); + } + +private: + enable_signal_t mEnableSignal; + enable_signal_t mVisibleSignal; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemCheckGL +// +// The LLMenuItemCheckGL is an extension of the LLMenuItemCallGL +// class, by allowing another method to be specified which determines +// if the menu item should consider itself checked as true or not. Be +// careful that the provided callback is fast - it needs to be VERY +// EFFICIENT because it may need to be checked a lot. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuItemCheckGL +: public LLMenuItemCallGL +{ +public: + struct Params : public LLInitParam::Block + { + Optional on_check; + Params() + : on_check("on_check") + {} + }; + +protected: + LLMenuItemCheckGL(const Params&); + friend class LLUICtrlFactory; +public: + + void initFromParams(const Params& p); + + virtual void onCommit( void ); + + virtual void setValue(const LLSD& value); + virtual LLSD getValue() const; + + // called to rebuild the draw label + virtual void buildDrawLabel( void ); + + boost::signals2::connection setCheckCallback( const enable_signal_t::slot_type& cb ) + { + return mCheckSignal.connect(cb); + } + +private: + enable_signal_t mCheckSignal; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuGL +// +// The Menu class represents a normal rectangular menu somewhere on +// screen. A Menu can have menu items (described above) or sub-menus +// attached to it. Sub-menus are implemented via a specialized +// menu-item type known as a branch. Since it's easy to do wrong, I've +// taken the branch functionality out of public view, and encapsulate +// it in the appendMenu() method. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// child widget registry +struct MenuRegistry : public LLChildRegistry +{ + LLSINGLETON_EMPTY_CTOR(MenuRegistry); +}; + + +class LLMenuGL +: public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional jump_key; + Optional horizontal_layout, + can_tear_off, + drop_shadow, + bg_visible, + create_jump_keys, + keep_fixed_size, + scrollable; + Optional max_scrollable_items; + Optional preferred_width; + Optional bg_color; + Optional shortcut_pad; + + Params() + : jump_key("jump_key", KEY_NONE), + horizontal_layout("horizontal_layout"), + can_tear_off("tear_off", false), + drop_shadow("drop_shadow", true), + bg_visible("bg_visible", true), + create_jump_keys("create_jump_keys", false), + keep_fixed_size("keep_fixed_size", false), + bg_color("bg_color", LLUIColorTable::instance().getColor( "MenuDefaultBgColor" )), + scrollable("scrollable", false), + max_scrollable_items("max_scrollable_items", U32_MAX), + preferred_width("preferred_width", U32_MAX), + shortcut_pad("shortcut_pad") + { + addSynonym(bg_visible, "opaque"); + addSynonym(bg_color, "color"); + addSynonym(can_tear_off, "can_tear_off"); + } + }; + + // my valid children are contained in MenuRegistry + typedef MenuRegistry child_registry_t; + + void initFromParams(const Params&); + + // 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; + + // for scrollable menus + typedef enum e_scrolling_direction + { + SD_UP = 0, + SD_DOWN = 1, + SD_BEGIN = 2, + SD_END = 3 + } EScrollingDirection; + +protected: + LLMenuGL(const LLMenuGL::Params& p); + friend class LLUICtrlFactory; + // let branching menu items use my protected traversal methods + friend class LLMenuItemBranchGL; +public: + virtual ~LLMenuGL( void ); + + void parseChildXML(LLXMLNodePtr child, LLView* parent); + + // LLView Functionality + /*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, F32 alpha); + /*virtual*/ void setVisible(bool visible); + /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); + /*virtual*/ void deleteAllChildren(); + /*virtual*/ void removeChild( LLView* ctrl); + /*virtual*/ bool postBuild(); + + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; + virtual bool handleAcceleratorKey(KEY key, MASK mask); + + LLMenuGL* findChildMenuByName(const std::string& name, bool recurse) const; + + bool clearHoverItem(); + + // return the name label + const std::string& getLabel( void ) const { return mLabel.getString(); } + void setLabel(const LLStringExplicit& label) { mLabel = label; } + + // background colors + void setBackgroundColor( const LLUIColor& color ) { mBackgroundColor = color; } + const LLUIColor& getBackgroundColor() const { return mBackgroundColor; } + void setBackgroundVisible( bool b ) { mBgVisible = b; } + void setCanTearOff(bool tear_off); + + // add a separator to this menu + virtual bool addSeparator(); + + // for branching menu items, bring sub menus up to root level of menu hierarchy + virtual void updateParent( LLView* parentp ); + + // setItemEnabled() - pass the name and the enable flag for a + // menu item. true will make sure it's enabled, false will disable + // it. + void setItemEnabled( const std::string& name, bool enable ); + + // propagate message to submenus + void setEnabledSubMenus(bool enable); + + void setItemVisible( const std::string& name, bool visible); + + void setItemLabel(const std::string &name, const std::string &label); + + // sets the left,bottom corner of menu, useful for popups + void setLeftAndBottom(S32 left, S32 bottom); + + virtual bool handleJumpKey(KEY key); + + virtual bool jumpKeysActive(); + + 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 ); + + // erase group of items from menu + void erase( S32 begin, S32 end, bool arrange = true ); + + // add new item at position + void insert( S32 begin, LLView * ctrl, bool arrange = true ); + + void setItemLastSelected(LLMenuItemGL* item); // must be in menu + U32 getItemCount(); // number of menu items + LLMenuItemGL* getItem(S32 number); // 0 = first item + LLMenuItemGL* getItem(std::string name); + LLMenuItemGL* getHighlightedItem(); + + LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled = true); + LLMenuItemGL* highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled = true); + + void buildDrawLabels(); + void createJumpKeys(); + + // Show popup at a specific location, in the spawn_view's coordinate frame + static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x = 0, S32 mouse_y = 0); + + // Whether to drop shadow menu bar + void setDropShadowed( const bool shadowed ); + + 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; } + + bool getCanTearOff() { return mTearOffItem != NULL; } + + KEY getJumpKey() const { return mJumpKey; } + void setJumpKey(KEY key) { mJumpKey = key; } + + static void setKeyboardMode(bool mode) { sKeyboardMode = mode; } + static bool getKeyboardMode() { return sKeyboardMode; } + + S32 getShortcutPad() { return mShortcutPad; } + + bool scrollItems(EScrollingDirection direction); + bool isScrollable() const { return mScrollable; } + + static class LLMenuHolderGL* sMenuContainer; + + void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; } + bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; } + + void setAlwaysShowMenu(bool show) { mAlwaysShowMenu = show; } + bool getAlwaysShowMenu() { return mAlwaysShowMenu; } + + // add a context menu branch + bool appendContextSubMenu(LLMenuGL *menu); + + const LLFontGL *getFont() const { return mFont; } + + 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 ); + + // Used in LLContextMenu and in LLTogleableMenu + // to add an item of context menu branch + bool addContextChild(LLView* view, S32 tab_group); + + // TODO: create accessor methods for these? + typedef std::list< LLMenuItemGL* > item_list_t; + item_list_t mItems; + LLMenuItemGL*mFirstVisibleItem; + LLMenuItemGL *mArrowUpItem, *mArrowDownItem; + + typedef std::map navigation_key_map_t; + navigation_key_map_t mJumpKeys; + S32 mLastMouseX; + S32 mLastMouseY; + S32 mMouseVelX; + S32 mMouseVelY; + U32 mMaxScrollableItems; + U32 mPreferredWidth; + bool mHorizontalLayout; + bool mScrollable; + bool mKeepFixedSize; + bool mNeedsArrange; + + // Font for top menu items only + const LLFontGL* mFont; + +private: + + + static LLColor4 sDefaultBackgroundColor; + static bool sKeyboardMode; + + bool mAlwaysShowMenu; + + LLUIColor mBackgroundColor; + bool mBgVisible; + LLHandle mParentMenuItem; + LLUIString mLabel; + bool mDropShadowed; // Whether to drop shadow + bool mHasSelection; + LLFrameTimer mFadeTimer; + LLTimer mScrollItemsTimer; + bool mTornOff; + class LLMenuItemTearOffGL* mTearOffItem; + class LLMenuItemBranchGL* mSpilloverBranch; + LLMenuGL* mSpilloverMenu; + KEY mJumpKey; + bool mCreateJumpKeys; + S32 mShortcutPad; + bool mResetScrollPositionOnShow; +}; // end class LLMenuGL + + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemBranchGL +// +// The LLMenuItemBranchGL represents a menu item that has a +// sub-menu. This is used to make cascading menus. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuItemBranchGL : public LLMenuItemGL +{ +public: + struct Params : public LLInitParam::Block + { + Optional branch; + }; + +protected: + LLMenuItemBranchGL(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLMenuItemBranchGL(); + + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; + virtual bool handleAcceleratorKey(KEY key, MASK mask); + + // check if we've used these accelerators already + virtual bool addToAcceleratorList(std::list *listp); + + // called to rebuild the draw label + virtual void buildDrawLabel( void ); + + virtual void onCommit( void ); + + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent); + virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); + + // set the hover status (called by it's menu) and if the object is + // active. This is used for behavior transfer. + virtual void setHighlight( bool highlight ); + + virtual bool handleKeyHere(KEY key, MASK mask); + + virtual bool isActive() const; + + virtual bool isOpen() const; + + LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); } + + virtual void updateBranchParent( LLView* parentp ); + + // LLView Functionality + virtual void onVisibilityChange( bool curVisibilityIn ); + + virtual void draw(); + + virtual void setEnabledSubMenus(bool enabled) { if (getBranch()) getBranch()->setEnabledSubMenus(enabled); } + + virtual void openMenu(); + + virtual LLView* getChildView(const std::string& name, bool recurse = true) const; + virtual LLView* findChildView(const std::string& name, bool recurse = true) const; + +private: + LLHandle mBranchHandle; +}; // end class LLMenuItemBranchGL + + +//----------------------------------------------------------------------------- +// class LLContextMenu +// A context menu +//----------------------------------------------------------------------------- + +class LLContextMenu +: public LLMenuGL +{ +public: + struct Params : public LLInitParam::Block + { + Params() + { + changeDefault(visible, false); + } + }; + +protected: + LLContextMenu(const Params& p); + friend class LLUICtrlFactory; + +public: + virtual ~LLContextMenu() {} + + // LLView Functionality + // can't set visibility directly, must call show or hide + virtual void setVisible (bool visible); + + virtual void show (S32 x, S32 y, LLView* spawning_view = NULL); + virtual void hide (); + + 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 addChild (LLView* view, S32 tab_group = 0); + + LLHandle getHandle() { return getDerivedHandle(); } + + LLView* getSpawningView() const { return mSpawningViewHandle.get(); } + void setSpawningView(LLHandle spawning_view) { mSpawningViewHandle = spawning_view; } + +protected: + bool mHoveredAnyItem; + LLMenuItemGL* mHoverItem; + LLRootHandle mHandle; + LLHandle mSpawningViewHandle; +}; + +//----------------------------------------------------------------------------- +// class LLContextMenuBranch +// A branch to another context menu +//----------------------------------------------------------------------------- +class LLContextMenuBranch : public LLMenuItemGL +{ +public: + struct Params : public LLInitParam::Block + { + Mandatory branch; + }; + + LLContextMenuBranch(const Params&); + + virtual ~LLContextMenuBranch(); + + // called to rebuild the draw label + virtual void buildDrawLabel( void ); + + // onCommit() - do the primary funcationality of the menu item. + virtual void onCommit( void ); + + LLContextMenu* getBranch() { return mBranch.get(); } + void setHighlight( bool highlight ); + +protected: + void showSubMenu(); + + LLHandle mBranch; +}; + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuBarGL +// +// A menu bar displays menus horizontally. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuBarGL : public LLMenuGL +{ +public: + struct Params : public LLInitParam::Block + {}; + LLMenuBarGL( const Params& p ); + virtual ~LLMenuBarGL(); + + /*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 handleDoubleClick(S32 x, S32 y, MASK mask); + + /*virtual*/ void draw(); + /*virtual*/ bool jumpKeysActive(); + + // add a vertical separator to this menu + virtual bool addSeparator(); + + // LLView Functionality + virtual bool handleHover( S32 x, S32 y, MASK mask ); + + // Returns x position of rightmost child, usually Help menu + S32 getRightmostMenuEdge(); + + 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; + bool mAltKeyTrigger; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuHolderGL +// +// High level view that serves as parent for all menus +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLMenuHolderGL : public LLPanel +{ +public: + struct Params : public LLInitParam::Block + {}; + LLMenuHolderGL(const Params& p); + virtual ~LLMenuHolderGL() {} + + virtual bool hideMenus(); + void reshape(S32 width, S32 height, bool called_from_parent = true); + void setCanHide(bool can_hide) { mCanHide = can_hide; } + + // LLView functionality + virtual void draw(); + 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(); } + 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; + + bool mCanHide; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLTearOffMenu +// +// Floater that hosts a menu +// https://wiki.lindenlab.com/mediawiki/index.php?title=LLTearOffMenu&oldid=81344 +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLTearOffMenu : public LLFloater +{ +public: + static LLTearOffMenu* create(LLMenuGL* menup); + virtual ~LLTearOffMenu(); + + virtual void draw(void); + virtual void onFocusReceived(); + virtual void onFocusLost(); + virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent); + virtual bool handleKeyHere(KEY key, MASK mask); + virtual void translate(S32 x, S32 y); + + void updateSize(); + +private: + LLTearOffMenu(LLMenuGL* menup); + + void closeTearOff(); + + LLView* mOldParent; + LLMenuGL* mMenu; + S32 mTargetHeight; + bool mQuitRequested; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemTearOffGL +// +// This class represents a separator. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMenuItemTearOffGL : public LLMenuItemGL +{ +public: + struct Params : public LLInitParam::Block + {}; + + LLMenuItemTearOffGL( const Params& ); + + virtual void onCommit(void); + virtual void draw(void); + virtual U32 getNominalHeight() const; + + LLFloater* getParentFloater(); +}; + + +// *TODO: this is currently working, so finish implementation +class LLEditMenuHandlerMgr +{ +public: + LLEditMenuHandlerMgr& getInstance() { + static LLEditMenuHandlerMgr instance; + return instance; + } + virtual ~LLEditMenuHandlerMgr() {} +private: + LLEditMenuHandlerMgr() {}; +}; + + +// *TODO: Eliminate +// For backwards compatability only; generally just use boost::bind +class view_listener_t : public boost::signals2::trackable +{ +public: + virtual bool handleEvent(const LLSD& userdata) = 0; + view_listener_t() { sListeners.insert(this); } + virtual ~view_listener_t() { sListeners.erase(this); } + + static void addEnable(view_listener_t* listener, const std::string& name) + { + LLUICtrl::EnableCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2)); + } + + static void addCommit(view_listener_t* listener, const std::string& name) + { + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2)); + } + + static void addMenu(view_listener_t* listener, const std::string& name) + { + // For now, add to both click and enable registries + addEnable(listener, name); + addCommit(listener, name); + } + + static void cleanup() + { + listener_vector_t listeners(sListeners.begin(), sListeners.end()); + sListeners.clear(); + + std::for_each(listeners.begin(), listeners.end(), DeletePointer()); + listeners.clear(); + } + +private: + typedef std::set listener_map_t; + typedef std::vector listener_vector_t; + static listener_map_t sListeners; +}; + +#endif // LL_LLMENUGL_H diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index 087ac325c8..c5c31f7252 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -1,348 +1,348 @@ -/** - * @file llmodaldialog.cpp - * @brief LLModalDialog base class - * - * $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$ - */ - -#include "linden_common.h" - -#include "llmodaldialog.h" - -#include "llfocusmgr.h" -#include "v4color.h" -#include "v2math.h" -#include "llui.h" -#include "llwindow.h" -#include "llkeyboard.h" -#include "llmenugl.h" -// static -std::list LLModalDialog::sModalStack; - -LLModalDialog::LLModalDialog( const LLSD& key, bool modal ) - : LLFloater(key), - mModal( modal ) -{ - if (modal) - { - setCanMinimize(false); - setCanClose(false); - } - setVisible( false ); - setBackgroundVisible(true); - setBackgroundOpaque(true); - centerOnScreen(); // default position - mCloseSignal.connect(boost::bind(&LLModalDialog::stopModal, this)); -} - -LLModalDialog::~LLModalDialog() -{ - // don't unlock focus unless we have it - if (gFocusMgr.childHasKeyboardFocus(this)) - { - gFocusMgr.unlockFocus(); - } - - std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); - if (iter != sModalStack.end()) - { - LL_ERRS() << "Attempt to delete dialog while still in sModalStack!" << LL_ENDL; - } - - LLUI::getInstance()->removePopup(this); -} - -// virtual -bool LLModalDialog::postBuild() -{ - return LLFloater::postBuild(); -} - -// virtual -void LLModalDialog::openFloater(const LLSD& key) -{ - // SJB: Hack! Make sure we don't ever host a modal dialog - LLMultiFloater* thost = LLFloater::getFloaterHost(); - LLFloater::setFloaterHost(NULL); - LLFloater::openFloater(key); - LLFloater::setFloaterHost(thost); -} - -void LLModalDialog::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLFloater::reshape(width, height, called_from_parent); - centerOnScreen(); -} - -// virtual -void LLModalDialog::onOpen(const LLSD& key) -{ - if (mModal) - { - // If Modal, Hide the active modal dialog - if (!sModalStack.empty()) - { - LLModalDialog* front = sModalStack.front(); - if (front != this) - { - front->setVisible(false); - } - } - - // This is a modal dialog. It sucks up all mouse and keyboard operations. - gFocusMgr.setMouseCapture( this ); - LLUI::getInstance()->addPopup(this); - setFocus(true); - - std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); - if (iter != sModalStack.end()) - { - // if already present, we want to move it to front. - sModalStack.erase(iter); - } - - sModalStack.push_front(this); - } -} - -void LLModalDialog::stopModal() -{ - gFocusMgr.unlockFocus(); - gFocusMgr.releaseFocusIfNeeded( this ); - - if (mModal) - { - std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); - if (iter != sModalStack.end()) - { - sModalStack.erase(iter); - } - else - { - LL_WARNS() << "LLModalDialog::stopModal not in list!" << LL_ENDL; - } - } - if (!sModalStack.empty()) - { - LLModalDialog* front = sModalStack.front(); - front->setVisible(true); - } -} - - -void LLModalDialog::setVisible( bool visible ) -{ - if (mModal) - { - if( visible ) - { - // This is a modal dialog. It sucks up all mouse and keyboard operations. - gFocusMgr.setMouseCapture( this ); - - // The dialog view is a root view - LLUI::getInstance()->addPopup(this); - setFocus( true ); - } - else - { - gFocusMgr.releaseFocusIfNeeded( this ); - } - } - - LLFloater::setVisible( visible ); -} - -bool LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask) -{ - LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu(); - if (popup_menu != NULL) - { - S32 mx, my; - LLUI::getInstance()->getMousePositionScreen(&mx, &my); - LLRect menu_screen_rc = popup_menu->calcScreenRect(); - if(!menu_screen_rc.pointInRect(mx, my)) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - } - - if (mModal) - { - if (!LLFloater::handleMouseDown(x, y, mask)) - { - // Click was outside the panel - make_ui_sound("UISndInvalidOp"); - } - } - else - { - LLFloater::handleMouseDown(x, y, mask); - } - - - return true; -} - -bool LLModalDialog::handleHover(S32 x, S32 y, MASK mask) -{ - if( childrenHandleHover(x, y, mask) == NULL ) - { - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL; - } - - LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu(); - if (popup_menu != NULL) - { - S32 mx, my; - LLUI::getInstance()->getMousePositionScreen(&mx, &my); - LLRect menu_screen_rc = popup_menu->calcScreenRect(); - if(menu_screen_rc.pointInRect(mx, my)) - { - S32 local_x = mx - popup_menu->getRect().mLeft; - S32 local_y = my - popup_menu->getRect().mBottom; - popup_menu->handleHover(local_x, local_y, mask); - gFocusMgr.setMouseCapture(NULL); - } - } - - return true; -} - -bool LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask) -{ - childrenHandleMouseUp(x, y, mask); - return true; -} - -bool LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - childrenHandleScrollWheel(x, y, clicks); - return true; -} - -bool LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - if (!LLFloater::handleDoubleClick(x, y, mask)) - { - // Click outside the panel - make_ui_sound("UISndInvalidOp"); - } - return true; -} - -bool LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - LLMenuGL::sMenuContainer->hideMenus(); - childrenHandleRightMouseDown(x, y, mask); - return true; -} - - -bool LLModalDialog::handleKeyHere(KEY key, MASK mask ) -{ - LLFloater::handleKeyHere(key, mask ); - - if (mModal) - { - // Suck up all keystokes except CTRL-Q. - bool is_quit = ('Q' == key) && (MASK_CONTROL == mask); - return !is_quit; - } - else - { - // don't process escape key until message box has been on screen a minimal amount of time - // to avoid accidentally destroying the message box when user is hitting escape at the time it appears - bool enough_time_elapsed = mVisibleTime.getElapsedTimeF32() > 1.0f; - if (enough_time_elapsed && key == KEY_ESCAPE) - { - closeFloater(); - return true; - } - return false; - } -} - -// virtual -void LLModalDialog::draw() -{ - static LLUIColor shadow_color = LLUIColorTable::instance().getColor("ColorDropShadow"); - - gl_drop_shadow( 0, getRect().getHeight(), getRect().getWidth(), 0, - shadow_color, DROP_SHADOW_FLOATER); - - LLFloater::draw(); - - // Focus retrieval moved to LLFloaterView::refresh() -} - -void LLModalDialog::centerOnScreen() -{ - LLVector2 window_size = LLUI::getInstance()->getWindowSize(); - centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY]))); -} - - -// static -void LLModalDialog::onAppFocusLost() -{ - if( !sModalStack.empty() ) - { - LLModalDialog* instance = LLModalDialog::sModalStack.front(); - if( gFocusMgr.childHasMouseCapture( instance ) ) - { - gFocusMgr.setMouseCapture( NULL ); - } - - instance->setFocus(false); - } -} - -// static -void LLModalDialog::onAppFocusGained() -{ - if( !sModalStack.empty() ) - { - LLModalDialog* instance = LLModalDialog::sModalStack.front(); - - // This is a modal dialog. It sucks up all mouse and keyboard operations. - gFocusMgr.setMouseCapture( instance ); - instance->setFocus(true); - LLUI::getInstance()->addPopup(instance); - - instance->centerOnScreen(); - } -} - -void LLModalDialog::shutdownModals() -{ - // This method is only for use during app shutdown. ~LLModalDialog() - // checks sModalStack, and if the dialog instance is still there, it - // crumps with "Attempt to delete dialog while still in sModalStack!" But - // at app shutdown, all bets are off. If the user asks to shut down the - // app, we shouldn't have to care WHAT's open. Put differently, if a modal - // dialog is so crucial that we can't let the user terminate until s/he - // addresses it, we should reject a termination request. The current state - // of affairs is that we accept it, but then produce an LL_ERRS() popup that - // simply makes our software look unreliable. - sModalStack.clear(); -} +/** + * @file llmodaldialog.cpp + * @brief LLModalDialog base class + * + * $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$ + */ + +#include "linden_common.h" + +#include "llmodaldialog.h" + +#include "llfocusmgr.h" +#include "v4color.h" +#include "v2math.h" +#include "llui.h" +#include "llwindow.h" +#include "llkeyboard.h" +#include "llmenugl.h" +// static +std::list LLModalDialog::sModalStack; + +LLModalDialog::LLModalDialog( const LLSD& key, bool modal ) + : LLFloater(key), + mModal( modal ) +{ + if (modal) + { + setCanMinimize(false); + setCanClose(false); + } + setVisible( false ); + setBackgroundVisible(true); + setBackgroundOpaque(true); + centerOnScreen(); // default position + mCloseSignal.connect(boost::bind(&LLModalDialog::stopModal, this)); +} + +LLModalDialog::~LLModalDialog() +{ + // don't unlock focus unless we have it + if (gFocusMgr.childHasKeyboardFocus(this)) + { + gFocusMgr.unlockFocus(); + } + + std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); + if (iter != sModalStack.end()) + { + LL_ERRS() << "Attempt to delete dialog while still in sModalStack!" << LL_ENDL; + } + + LLUI::getInstance()->removePopup(this); +} + +// virtual +bool LLModalDialog::postBuild() +{ + return LLFloater::postBuild(); +} + +// virtual +void LLModalDialog::openFloater(const LLSD& key) +{ + // SJB: Hack! Make sure we don't ever host a modal dialog + LLMultiFloater* thost = LLFloater::getFloaterHost(); + LLFloater::setFloaterHost(NULL); + LLFloater::openFloater(key); + LLFloater::setFloaterHost(thost); +} + +void LLModalDialog::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLFloater::reshape(width, height, called_from_parent); + centerOnScreen(); +} + +// virtual +void LLModalDialog::onOpen(const LLSD& key) +{ + if (mModal) + { + // If Modal, Hide the active modal dialog + if (!sModalStack.empty()) + { + LLModalDialog* front = sModalStack.front(); + if (front != this) + { + front->setVisible(false); + } + } + + // This is a modal dialog. It sucks up all mouse and keyboard operations. + gFocusMgr.setMouseCapture( this ); + LLUI::getInstance()->addPopup(this); + setFocus(true); + + std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); + if (iter != sModalStack.end()) + { + // if already present, we want to move it to front. + sModalStack.erase(iter); + } + + sModalStack.push_front(this); + } +} + +void LLModalDialog::stopModal() +{ + gFocusMgr.unlockFocus(); + gFocusMgr.releaseFocusIfNeeded( this ); + + if (mModal) + { + std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); + if (iter != sModalStack.end()) + { + sModalStack.erase(iter); + } + else + { + LL_WARNS() << "LLModalDialog::stopModal not in list!" << LL_ENDL; + } + } + if (!sModalStack.empty()) + { + LLModalDialog* front = sModalStack.front(); + front->setVisible(true); + } +} + + +void LLModalDialog::setVisible( bool visible ) +{ + if (mModal) + { + if( visible ) + { + // This is a modal dialog. It sucks up all mouse and keyboard operations. + gFocusMgr.setMouseCapture( this ); + + // The dialog view is a root view + LLUI::getInstance()->addPopup(this); + setFocus( true ); + } + else + { + gFocusMgr.releaseFocusIfNeeded( this ); + } + } + + LLFloater::setVisible( visible ); +} + +bool LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask) +{ + LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu(); + if (popup_menu != NULL) + { + S32 mx, my; + LLUI::getInstance()->getMousePositionScreen(&mx, &my); + LLRect menu_screen_rc = popup_menu->calcScreenRect(); + if(!menu_screen_rc.pointInRect(mx, my)) + { + LLMenuGL::sMenuContainer->hideMenus(); + } + } + + if (mModal) + { + if (!LLFloater::handleMouseDown(x, y, mask)) + { + // Click was outside the panel + make_ui_sound("UISndInvalidOp"); + } + } + else + { + LLFloater::handleMouseDown(x, y, mask); + } + + + return true; +} + +bool LLModalDialog::handleHover(S32 x, S32 y, MASK mask) +{ + if( childrenHandleHover(x, y, mask) == NULL ) + { + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL; + } + + LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu(); + if (popup_menu != NULL) + { + S32 mx, my; + LLUI::getInstance()->getMousePositionScreen(&mx, &my); + LLRect menu_screen_rc = popup_menu->calcScreenRect(); + if(menu_screen_rc.pointInRect(mx, my)) + { + S32 local_x = mx - popup_menu->getRect().mLeft; + S32 local_y = my - popup_menu->getRect().mBottom; + popup_menu->handleHover(local_x, local_y, mask); + gFocusMgr.setMouseCapture(NULL); + } + } + + return true; +} + +bool LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask) +{ + childrenHandleMouseUp(x, y, mask); + return true; +} + +bool LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + childrenHandleScrollWheel(x, y, clicks); + return true; +} + +bool LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + if (!LLFloater::handleDoubleClick(x, y, mask)) + { + // Click outside the panel + make_ui_sound("UISndInvalidOp"); + } + return true; +} + +bool LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLMenuGL::sMenuContainer->hideMenus(); + childrenHandleRightMouseDown(x, y, mask); + return true; +} + + +bool LLModalDialog::handleKeyHere(KEY key, MASK mask ) +{ + LLFloater::handleKeyHere(key, mask ); + + if (mModal) + { + // Suck up all keystokes except CTRL-Q. + bool is_quit = ('Q' == key) && (MASK_CONTROL == mask); + return !is_quit; + } + else + { + // don't process escape key until message box has been on screen a minimal amount of time + // to avoid accidentally destroying the message box when user is hitting escape at the time it appears + bool enough_time_elapsed = mVisibleTime.getElapsedTimeF32() > 1.0f; + if (enough_time_elapsed && key == KEY_ESCAPE) + { + closeFloater(); + return true; + } + return false; + } +} + +// virtual +void LLModalDialog::draw() +{ + static LLUIColor shadow_color = LLUIColorTable::instance().getColor("ColorDropShadow"); + + gl_drop_shadow( 0, getRect().getHeight(), getRect().getWidth(), 0, + shadow_color, DROP_SHADOW_FLOATER); + + LLFloater::draw(); + + // Focus retrieval moved to LLFloaterView::refresh() +} + +void LLModalDialog::centerOnScreen() +{ + LLVector2 window_size = LLUI::getInstance()->getWindowSize(); + centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY]))); +} + + +// static +void LLModalDialog::onAppFocusLost() +{ + if( !sModalStack.empty() ) + { + LLModalDialog* instance = LLModalDialog::sModalStack.front(); + if( gFocusMgr.childHasMouseCapture( instance ) ) + { + gFocusMgr.setMouseCapture( NULL ); + } + + instance->setFocus(false); + } +} + +// static +void LLModalDialog::onAppFocusGained() +{ + if( !sModalStack.empty() ) + { + LLModalDialog* instance = LLModalDialog::sModalStack.front(); + + // This is a modal dialog. It sucks up all mouse and keyboard operations. + gFocusMgr.setMouseCapture( instance ); + instance->setFocus(true); + LLUI::getInstance()->addPopup(instance); + + instance->centerOnScreen(); + } +} + +void LLModalDialog::shutdownModals() +{ + // This method is only for use during app shutdown. ~LLModalDialog() + // checks sModalStack, and if the dialog instance is still there, it + // crumps with "Attempt to delete dialog while still in sModalStack!" But + // at app shutdown, all bets are off. If the user asks to shut down the + // app, we shouldn't have to care WHAT's open. Put differently, if a modal + // dialog is so crucial that we can't let the user terminate until s/he + // addresses it, we should reject a termination request. The current state + // of affairs is that we accept it, but then produce an LL_ERRS() popup that + // simply makes our software look unreliable. + sModalStack.clear(); +} diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h index 37637bcb18..58c253c3f4 100644 --- a/indra/llui/llmodaldialog.h +++ b/indra/llui/llmodaldialog.h @@ -1,83 +1,83 @@ -/** - * @file llmodaldialog.h - * @brief LLModalDialog base class - * - * $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_LLMODALDIALOG_H -#define LL_LLMODALDIALOG_H - -#include "llfloater.h" -#include "llframetimer.h" - -class LLModalDialog; - -// By default, a ModalDialog is modal, i.e. no other window can have focus -// However, for the sake of code reuse and simplicity, if mModal == false, -// the dialog behaves like a normal floater -// https://wiki.lindenlab.com/mediawiki/index.php?title=LLModalDialog&oldid=81385 -class LLModalDialog : public LLFloater -{ -public: - LLModalDialog( const LLSD& key, bool modal = true ); - virtual ~LLModalDialog(); - - /*virtual*/ bool postBuild(); - - /*virtual*/ void openFloater(const LLSD& key = LLSD()); - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - - /*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 handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleKeyHere(KEY key, MASK mask ); - - /*virtual*/ void setVisible(bool visible); - /*virtual*/ void draw(); - - bool isModal() const { return mModal; } - void stopModal(); - - static void onAppFocusLost(); - static void onAppFocusGained(); - - static S32 activeCount() { return sModalStack.size(); } - static void shutdownModals(); - -protected: - void centerOnScreen(); - -private: - - LLFrameTimer mVisibleTime; - const bool mModal; - - static std::list sModalStack; // Top of stack is currently being displayed -}; - -#endif // LL_LLMODALDIALOG_H +/** + * @file llmodaldialog.h + * @brief LLModalDialog base class + * + * $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_LLMODALDIALOG_H +#define LL_LLMODALDIALOG_H + +#include "llfloater.h" +#include "llframetimer.h" + +class LLModalDialog; + +// By default, a ModalDialog is modal, i.e. no other window can have focus +// However, for the sake of code reuse and simplicity, if mModal == false, +// the dialog behaves like a normal floater +// https://wiki.lindenlab.com/mediawiki/index.php?title=LLModalDialog&oldid=81385 +class LLModalDialog : public LLFloater +{ +public: + LLModalDialog( const LLSD& key, bool modal = true ); + virtual ~LLModalDialog(); + + /*virtual*/ bool postBuild(); + + /*virtual*/ void openFloater(const LLSD& key = LLSD()); + /*virtual*/ void onOpen(const LLSD& key); + + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + + /*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 handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask ); + + /*virtual*/ void setVisible(bool visible); + /*virtual*/ void draw(); + + bool isModal() const { return mModal; } + void stopModal(); + + static void onAppFocusLost(); + static void onAppFocusGained(); + + static S32 activeCount() { return sModalStack.size(); } + static void shutdownModals(); + +protected: + void centerOnScreen(); + +private: + + LLFrameTimer mVisibleTime; + const bool mModal; + + static std::list sModalStack; // Top of stack is currently being displayed +}; + +#endif // LL_LLMODALDIALOG_H diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp index 08e9c763f4..a7f9b8b2d9 100644 --- a/indra/llui/llmultifloater.cpp +++ b/indra/llui/llmultifloater.cpp @@ -1,522 +1,522 @@ -/** - * @file llmultifloater.cpp - * @brief LLFloater that hosts other floaters - * - * $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$ - */ - -// Floating "windows" within the GL display, like the inventory floater, -// mini-map floater, etc. - -#include "linden_common.h" - -#include "llmultifloater.h" -#include "llresizehandle.h" - -// -// LLMultiFloater -// - -LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params) - : LLFloater(key), - mTabContainer(NULL), - mTabPos(LLTabContainer::TOP), - mAutoResize(true), - mOrigMinWidth(params.min_width), - mOrigMinHeight(params.min_height) -{ -} - -void LLMultiFloater::buildTabContainer() -{ - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - - LLTabContainer::Params p; - p.name(std::string("Preview Tabs")); - p.rect(LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - floater_header_size, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0)); - p.tab_position(mTabPos); - p.follows.flags(FOLLOWS_ALL); - p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this)); - - mTabContainer = LLUICtrlFactory::create(p); - addChild(mTabContainer); - - if (isResizable()) - { - mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH); - } -} - -void LLMultiFloater::onClose(bool app_quitting) -{ - if(isMinimized()) - { - setMinimized(false); - } - LLFloater::onClose(app_quitting); -} - -void LLMultiFloater::draw() -{ - if (mTabContainer->getTabCount() == 0) - { - //RN: could this potentially crash in draw hierarchy? - closeFloater(); - } - else - { - LLFloater::draw(); - } -} - -bool LLMultiFloater::closeAllFloaters() -{ - S32 tabToClose = 0; - S32 lastTabCount = mTabContainer->getTabCount(); - while (tabToClose < mTabContainer->getTabCount()) - { - LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose); - first_floater->closeFloater(); - if(lastTabCount == mTabContainer->getTabCount()) - { - //Tab did not actually close, possibly due to a pending Save Confirmation dialog.. - //so try and close the next one in the list... - tabToClose++; - } - else - { - //Tab closed ok. - lastTabCount = mTabContainer->getTabCount(); - } - } - if( mTabContainer->getTabCount() != 0 ) - return false; // Couldn't close all the tabs (pending save dialog?) so return false. - return true; //else all tabs were successfully closed... -} - -void LLMultiFloater::growToFit(S32 content_width, S32 content_height) -{ - static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; - S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2); - S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height); - - if (isMinimized()) - { - LLRect newrect; - newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height); - setExpandedRect(newrect); - } - else - { - S32 old_height = getRect().getHeight(); - reshape(new_width, new_height); - // keep top left corner in same position - translate(0, old_height - new_height); - } -} - -/** - void addFloater(LLFloater* floaterp, bool select_added_floater) - - Adds the LLFloater pointed to by floaterp to this. - If floaterp is already hosted by this, then it is re-added to get - new titles, etc. - If select_added_floater is true, the LLFloater pointed to by floaterp will - become the selected tab in this - - Affects: mTabContainer, floaterp -**/ -void LLMultiFloater::addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point) -{ - if (!floaterp) - { - return; - } - - if (!mTabContainer) - { - LL_ERRS() << "Tab Container used without having been initialized." << LL_ENDL; - return; - } - - if (floaterp->getHost() == this) - { - // already hosted by me, remove - // do this so we get updated title, etc. - mFloaterDataMap.erase(floaterp->getHandle()); - mTabContainer->removeTabPanel(floaterp); - } - else if (floaterp->getHost()) - { - // floaterp is hosted by somebody else and - // this is adding it, so remove it from its old host - floaterp->getHost()->removeFloater(floaterp); - } - else if (floaterp->getParent() == gFloaterView) - { - // rehost preview floater as child panel - gFloaterView->removeChild(floaterp); - } - - // store original configuration - LLFloaterData floater_data; - floater_data.mWidth = floaterp->getRect().getWidth(); - floater_data.mHeight = floaterp->getRect().getHeight(); - floater_data.mCanMinimize = floaterp->isMinimizeable(); - floater_data.mCanResize = floaterp->isResizable(); - floater_data.mSaveRect = floaterp->mSaveRect; - - // remove minimize and close buttons - floaterp->setCanMinimize(false); - floaterp->setCanResize(false); - floaterp->setCanDrag(false); - floaterp->mSaveRect = false; - floaterp->storeRectControl(); - // avoid double rendering of floater background (makes it more opaque) - floaterp->setBackgroundVisible(false); - - if (mAutoResize) - { - growToFit(floater_data.mWidth, floater_data.mHeight); - } - - //add the panel, add it to proper maps - mTabContainer->addTabPanel( - LLTabContainer::TabPanelParams() - .panel(floaterp) - .label(floaterp->getShortTitle()) - .insert_at(insertion_point)); - mFloaterDataMap[floaterp->getHandle()] = floater_data; - - updateResizeLimits(); - - if ( select_added_floater ) - { - mTabContainer->selectTabPanel(floaterp); - } - else - { - // reassert visible tab (hiding new floater if necessary) - mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex()); - } - - floaterp->setHost(this); - if (isMinimized()) - { - floaterp->setVisible(false); - } - - // Tabs sometimes overlap resize handle - moveResizeHandlesToFront(); -} - -void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp) -{ - S32 index = mTabContainer->getIndexForPanel(floaterp); - if (index != -1) - { - mTabContainer->setPanelTitle(index, floaterp->getShortTitle()); - } -} - - -/** - bool selectFloater(LLFloater* floaterp) - - If the LLFloater pointed to by floaterp is hosted by this, - then its tab is selected and returns true. Otherwise returns false. - - Affects: mTabContainer -**/ -bool LLMultiFloater::selectFloater(LLFloater* floaterp) -{ - return mTabContainer->selectTabPanel(floaterp); -} - -// virtual -void LLMultiFloater::selectNextFloater() -{ - mTabContainer->selectNextTab(); -} - -// virtual -void LLMultiFloater::selectPrevFloater() -{ - mTabContainer->selectPrevTab(); -} - -void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point) -{ - if(!floaterp) return; - // we won't select a panel that already is selected - // it is hard to do this internally to tab container - // as tab selection is handled via index and the tab at a given - // index might have changed - if (floaterp != mTabContainer->getCurrentPanel() && - !mTabContainer->selectTabPanel(floaterp)) - { - addFloater(floaterp, true, insertion_point); - } -} - -void LLMultiFloater::removeFloater(LLFloater* floaterp) -{ - if (!floaterp || floaterp->getHost() != this ) - return; - - floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle()); - if (found_data_it != mFloaterDataMap.end()) - { - LLFloaterData& floater_data = found_data_it->second; - floaterp->setCanMinimize(floater_data.mCanMinimize); - floaterp->mSaveRect = floater_data.mSaveRect; - if (!floater_data.mCanResize) - { - // restore original size - floaterp->reshape(floater_data.mWidth, floater_data.mHeight); - } - floaterp->setCanResize(floater_data.mCanResize); - mFloaterDataMap.erase(found_data_it); - } - mTabContainer->removeTabPanel(floaterp); - floaterp->setBackgroundVisible(true); - floaterp->setCanDrag(true); - floaterp->setHost(NULL); - floaterp->applyRectControl(); - - updateResizeLimits(); - - tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false); -} - -void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click) -{ - // default implementation does nothing -} - -void LLMultiFloater::tabClose() -{ - if (mTabContainer->getTabCount() == 0) - { - // no more children, close myself - closeFloater(); - } -} - -void LLMultiFloater::setVisible(bool visible) -{ - // *FIX: shouldn't have to do this, fix adding to minimized multifloater - LLFloater::setVisible(visible); - - if (mTabContainer) - { - LLPanel* cur_floaterp = mTabContainer->getCurrentPanel(); - - if (cur_floaterp) - { - cur_floaterp->setVisible(visible); - } - - // if no tab selected, and we're being shown, - // select last tab to be added - if (visible && !cur_floaterp) - { - mTabContainer->selectLastTab(); - } - } -} - -bool LLMultiFloater::handleKeyHere(KEY key, MASK mask) -{ - if (key == 'W' && mask == MASK_CONTROL) - { - LLFloater* floater = getActiveFloater(); - // is user closeable and is system closeable - if (floater && floater->canClose() && floater->isCloseable()) - { - floater->closeFloater(); - - // EXT-5695 (Tabbed IM window loses focus if close any tabs by Ctrl+W) - // bring back focus on tab container if there are any tab left - if(mTabContainer->getTabCount() > 0) - { - mTabContainer->setFocus(true); - } - } - return true; - } - - return LLFloater::handleKeyHere(key, mask); -} - -bool LLMultiFloater::addChild(LLView* child, S32 tab_group) -{ - LLTabContainer* tab_container = dynamic_cast(child); - if (tab_container) - { - // store pointer to tab container - setTabContainer(tab_container); - } - - // then go ahead and add child as usual - return LLFloater::addChild(child, tab_group); -} - -LLFloater* LLMultiFloater::getActiveFloater() -{ - return (LLFloater*)mTabContainer->getCurrentPanel(); -} - -S32 LLMultiFloater::getFloaterCount() -{ - return mTabContainer->getTabCount(); -} - -/** - bool isFloaterFlashing(LLFloater* floaterp) - - Returns true if the LLFloater pointed to by floaterp - is currently in a flashing state and is hosted by this. - False otherwise. - - Requires: floaterp != NULL -**/ -bool LLMultiFloater::isFloaterFlashing(LLFloater* floaterp) -{ - if ( floaterp && floaterp->getHost() == this ) - return mTabContainer->getTabPanelFlashing(floaterp); - - return false; -} - -/** - bool setFloaterFlashing(LLFloater* floaterp, bool flashing) - - Sets the current flashing state of the LLFloater pointed - to by floaterp to be the bool flashing if the LLFloater pointed - to by floaterp is hosted by this. - - Requires: floaterp != NULL -**/ -void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, bool flashing) -{ - if ( floaterp && floaterp->getHost() == this ) - mTabContainer->setTabPanelFlashing(floaterp, flashing); -} - -void LLMultiFloater::onTabSelected() -{ - LLFloater* floaterp = dynamic_cast(mTabContainer->getCurrentPanel()); - if (floaterp) - { - tabOpen(floaterp, true); - } -} - -void LLMultiFloater::setCanResize(bool can_resize) -{ - LLFloater::setCanResize(can_resize); - if (!mTabContainer) return; - if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM) - { - mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH); - } - else - { - mTabContainer->setRightTabBtnOffset(0); - } -} - -bool LLMultiFloater::postBuild() -{ - mCloseSignal.connect(boost::bind(&LLMultiFloater::closeAllFloaters, this)); - - // remember any original xml minimum size - getResizeLimits(&mOrigMinWidth, &mOrigMinHeight); - - if (mTabContainer) - { - return true; - } - - mTabContainer = getChild("Preview Tabs"); - - setCanResize(mResizable); - return true; -} - -void LLMultiFloater::updateResizeLimits() -{ - // initialize minimum size constraint to the original xml values. - S32 new_min_width = mOrigMinWidth; - S32 new_min_height = mOrigMinHeight; - - computeResizeLimits(new_min_width, new_min_height); - - setResizeLimits(new_min_width, new_min_height); - - S32 cur_height = getRect().getHeight(); - S32 new_width = llmax(getRect().getWidth(), new_min_width); - S32 new_height = llmax(getRect().getHeight(), new_min_height); - - if (isMinimized()) - { - const LLRect& expanded = getExpandedRect(); - LLRect newrect; - newrect.setLeftTopAndSize(expanded.mLeft, expanded.mTop, llmax(expanded.getWidth(), new_width), llmax(expanded.getHeight(), new_height)); - setExpandedRect(newrect); - } - else - { - reshape(new_width, new_height); - - // make sure upper left corner doesn't move - translate(0, cur_height - getRect().getHeight()); - - // make sure this window is visible on screen when it has been modified - // (tab added, etc) - gFloaterView->adjustToFitScreen(this, true); - } -} - -void LLMultiFloater::computeResizeLimits(S32& new_min_width, S32& new_min_height) -{ - static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; - - // possibly increase minimum size constraint due to children's minimums. - for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) - { - LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); - if (floaterp) - { - new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2); - new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); - } - } -} +/** + * @file llmultifloater.cpp + * @brief LLFloater that hosts other floaters + * + * $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$ + */ + +// Floating "windows" within the GL display, like the inventory floater, +// mini-map floater, etc. + +#include "linden_common.h" + +#include "llmultifloater.h" +#include "llresizehandle.h" + +// +// LLMultiFloater +// + +LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params) + : LLFloater(key), + mTabContainer(NULL), + mTabPos(LLTabContainer::TOP), + mAutoResize(true), + mOrigMinWidth(params.min_width), + mOrigMinHeight(params.min_height) +{ +} + +void LLMultiFloater::buildTabContainer() +{ + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + + LLTabContainer::Params p; + p.name(std::string("Preview Tabs")); + p.rect(LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - floater_header_size, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0)); + p.tab_position(mTabPos); + p.follows.flags(FOLLOWS_ALL); + p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this)); + + mTabContainer = LLUICtrlFactory::create(p); + addChild(mTabContainer); + + if (isResizable()) + { + mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH); + } +} + +void LLMultiFloater::onClose(bool app_quitting) +{ + if(isMinimized()) + { + setMinimized(false); + } + LLFloater::onClose(app_quitting); +} + +void LLMultiFloater::draw() +{ + if (mTabContainer->getTabCount() == 0) + { + //RN: could this potentially crash in draw hierarchy? + closeFloater(); + } + else + { + LLFloater::draw(); + } +} + +bool LLMultiFloater::closeAllFloaters() +{ + S32 tabToClose = 0; + S32 lastTabCount = mTabContainer->getTabCount(); + while (tabToClose < mTabContainer->getTabCount()) + { + LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose); + first_floater->closeFloater(); + if(lastTabCount == mTabContainer->getTabCount()) + { + //Tab did not actually close, possibly due to a pending Save Confirmation dialog.. + //so try and close the next one in the list... + tabToClose++; + } + else + { + //Tab closed ok. + lastTabCount = mTabContainer->getTabCount(); + } + } + if( mTabContainer->getTabCount() != 0 ) + return false; // Couldn't close all the tabs (pending save dialog?) so return false. + return true; //else all tabs were successfully closed... +} + +void LLMultiFloater::growToFit(S32 content_width, S32 content_height) +{ + static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2); + S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height); + + if (isMinimized()) + { + LLRect newrect; + newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height); + setExpandedRect(newrect); + } + else + { + S32 old_height = getRect().getHeight(); + reshape(new_width, new_height); + // keep top left corner in same position + translate(0, old_height - new_height); + } +} + +/** + void addFloater(LLFloater* floaterp, bool select_added_floater) + + Adds the LLFloater pointed to by floaterp to this. + If floaterp is already hosted by this, then it is re-added to get + new titles, etc. + If select_added_floater is true, the LLFloater pointed to by floaterp will + become the selected tab in this + + Affects: mTabContainer, floaterp +**/ +void LLMultiFloater::addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point) +{ + if (!floaterp) + { + return; + } + + if (!mTabContainer) + { + LL_ERRS() << "Tab Container used without having been initialized." << LL_ENDL; + return; + } + + if (floaterp->getHost() == this) + { + // already hosted by me, remove + // do this so we get updated title, etc. + mFloaterDataMap.erase(floaterp->getHandle()); + mTabContainer->removeTabPanel(floaterp); + } + else if (floaterp->getHost()) + { + // floaterp is hosted by somebody else and + // this is adding it, so remove it from its old host + floaterp->getHost()->removeFloater(floaterp); + } + else if (floaterp->getParent() == gFloaterView) + { + // rehost preview floater as child panel + gFloaterView->removeChild(floaterp); + } + + // store original configuration + LLFloaterData floater_data; + floater_data.mWidth = floaterp->getRect().getWidth(); + floater_data.mHeight = floaterp->getRect().getHeight(); + floater_data.mCanMinimize = floaterp->isMinimizeable(); + floater_data.mCanResize = floaterp->isResizable(); + floater_data.mSaveRect = floaterp->mSaveRect; + + // remove minimize and close buttons + floaterp->setCanMinimize(false); + floaterp->setCanResize(false); + floaterp->setCanDrag(false); + floaterp->mSaveRect = false; + floaterp->storeRectControl(); + // avoid double rendering of floater background (makes it more opaque) + floaterp->setBackgroundVisible(false); + + if (mAutoResize) + { + growToFit(floater_data.mWidth, floater_data.mHeight); + } + + //add the panel, add it to proper maps + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams() + .panel(floaterp) + .label(floaterp->getShortTitle()) + .insert_at(insertion_point)); + mFloaterDataMap[floaterp->getHandle()] = floater_data; + + updateResizeLimits(); + + if ( select_added_floater ) + { + mTabContainer->selectTabPanel(floaterp); + } + else + { + // reassert visible tab (hiding new floater if necessary) + mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex()); + } + + floaterp->setHost(this); + if (isMinimized()) + { + floaterp->setVisible(false); + } + + // Tabs sometimes overlap resize handle + moveResizeHandlesToFront(); +} + +void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp) +{ + S32 index = mTabContainer->getIndexForPanel(floaterp); + if (index != -1) + { + mTabContainer->setPanelTitle(index, floaterp->getShortTitle()); + } +} + + +/** + bool selectFloater(LLFloater* floaterp) + + If the LLFloater pointed to by floaterp is hosted by this, + then its tab is selected and returns true. Otherwise returns false. + + Affects: mTabContainer +**/ +bool LLMultiFloater::selectFloater(LLFloater* floaterp) +{ + return mTabContainer->selectTabPanel(floaterp); +} + +// virtual +void LLMultiFloater::selectNextFloater() +{ + mTabContainer->selectNextTab(); +} + +// virtual +void LLMultiFloater::selectPrevFloater() +{ + mTabContainer->selectPrevTab(); +} + +void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point) +{ + if(!floaterp) return; + // we won't select a panel that already is selected + // it is hard to do this internally to tab container + // as tab selection is handled via index and the tab at a given + // index might have changed + if (floaterp != mTabContainer->getCurrentPanel() && + !mTabContainer->selectTabPanel(floaterp)) + { + addFloater(floaterp, true, insertion_point); + } +} + +void LLMultiFloater::removeFloater(LLFloater* floaterp) +{ + if (!floaterp || floaterp->getHost() != this ) + return; + + floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle()); + if (found_data_it != mFloaterDataMap.end()) + { + LLFloaterData& floater_data = found_data_it->second; + floaterp->setCanMinimize(floater_data.mCanMinimize); + floaterp->mSaveRect = floater_data.mSaveRect; + if (!floater_data.mCanResize) + { + // restore original size + floaterp->reshape(floater_data.mWidth, floater_data.mHeight); + } + floaterp->setCanResize(floater_data.mCanResize); + mFloaterDataMap.erase(found_data_it); + } + mTabContainer->removeTabPanel(floaterp); + floaterp->setBackgroundVisible(true); + floaterp->setCanDrag(true); + floaterp->setHost(NULL); + floaterp->applyRectControl(); + + updateResizeLimits(); + + tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false); +} + +void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click) +{ + // default implementation does nothing +} + +void LLMultiFloater::tabClose() +{ + if (mTabContainer->getTabCount() == 0) + { + // no more children, close myself + closeFloater(); + } +} + +void LLMultiFloater::setVisible(bool visible) +{ + // *FIX: shouldn't have to do this, fix adding to minimized multifloater + LLFloater::setVisible(visible); + + if (mTabContainer) + { + LLPanel* cur_floaterp = mTabContainer->getCurrentPanel(); + + if (cur_floaterp) + { + cur_floaterp->setVisible(visible); + } + + // if no tab selected, and we're being shown, + // select last tab to be added + if (visible && !cur_floaterp) + { + mTabContainer->selectLastTab(); + } + } +} + +bool LLMultiFloater::handleKeyHere(KEY key, MASK mask) +{ + if (key == 'W' && mask == MASK_CONTROL) + { + LLFloater* floater = getActiveFloater(); + // is user closeable and is system closeable + if (floater && floater->canClose() && floater->isCloseable()) + { + floater->closeFloater(); + + // EXT-5695 (Tabbed IM window loses focus if close any tabs by Ctrl+W) + // bring back focus on tab container if there are any tab left + if(mTabContainer->getTabCount() > 0) + { + mTabContainer->setFocus(true); + } + } + return true; + } + + return LLFloater::handleKeyHere(key, mask); +} + +bool LLMultiFloater::addChild(LLView* child, S32 tab_group) +{ + LLTabContainer* tab_container = dynamic_cast(child); + if (tab_container) + { + // store pointer to tab container + setTabContainer(tab_container); + } + + // then go ahead and add child as usual + return LLFloater::addChild(child, tab_group); +} + +LLFloater* LLMultiFloater::getActiveFloater() +{ + return (LLFloater*)mTabContainer->getCurrentPanel(); +} + +S32 LLMultiFloater::getFloaterCount() +{ + return mTabContainer->getTabCount(); +} + +/** + bool isFloaterFlashing(LLFloater* floaterp) + + Returns true if the LLFloater pointed to by floaterp + is currently in a flashing state and is hosted by this. + False otherwise. + + Requires: floaterp != NULL +**/ +bool LLMultiFloater::isFloaterFlashing(LLFloater* floaterp) +{ + if ( floaterp && floaterp->getHost() == this ) + return mTabContainer->getTabPanelFlashing(floaterp); + + return false; +} + +/** + bool setFloaterFlashing(LLFloater* floaterp, bool flashing) + + Sets the current flashing state of the LLFloater pointed + to by floaterp to be the bool flashing if the LLFloater pointed + to by floaterp is hosted by this. + + Requires: floaterp != NULL +**/ +void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, bool flashing) +{ + if ( floaterp && floaterp->getHost() == this ) + mTabContainer->setTabPanelFlashing(floaterp, flashing); +} + +void LLMultiFloater::onTabSelected() +{ + LLFloater* floaterp = dynamic_cast(mTabContainer->getCurrentPanel()); + if (floaterp) + { + tabOpen(floaterp, true); + } +} + +void LLMultiFloater::setCanResize(bool can_resize) +{ + LLFloater::setCanResize(can_resize); + if (!mTabContainer) return; + if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM) + { + mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH); + } + else + { + mTabContainer->setRightTabBtnOffset(0); + } +} + +bool LLMultiFloater::postBuild() +{ + mCloseSignal.connect(boost::bind(&LLMultiFloater::closeAllFloaters, this)); + + // remember any original xml minimum size + getResizeLimits(&mOrigMinWidth, &mOrigMinHeight); + + if (mTabContainer) + { + return true; + } + + mTabContainer = getChild("Preview Tabs"); + + setCanResize(mResizable); + return true; +} + +void LLMultiFloater::updateResizeLimits() +{ + // initialize minimum size constraint to the original xml values. + S32 new_min_width = mOrigMinWidth; + S32 new_min_height = mOrigMinHeight; + + computeResizeLimits(new_min_width, new_min_height); + + setResizeLimits(new_min_width, new_min_height); + + S32 cur_height = getRect().getHeight(); + S32 new_width = llmax(getRect().getWidth(), new_min_width); + S32 new_height = llmax(getRect().getHeight(), new_min_height); + + if (isMinimized()) + { + const LLRect& expanded = getExpandedRect(); + LLRect newrect; + newrect.setLeftTopAndSize(expanded.mLeft, expanded.mTop, llmax(expanded.getWidth(), new_width), llmax(expanded.getHeight(), new_height)); + setExpandedRect(newrect); + } + else + { + reshape(new_width, new_height); + + // make sure upper left corner doesn't move + translate(0, cur_height - getRect().getHeight()); + + // make sure this window is visible on screen when it has been modified + // (tab added, etc) + gFloaterView->adjustToFitScreen(this, true); + } +} + +void LLMultiFloater::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + + // possibly increase minimum size constraint due to children's minimums. + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); + if (floaterp) + { + new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2); + new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); + } + } +} diff --git a/indra/llui/llmultifloater.h b/indra/llui/llmultifloater.h index 82cff6084a..eb0f917695 100644 --- a/indra/llui/llmultifloater.h +++ b/indra/llui/llmultifloater.h @@ -1,102 +1,102 @@ -/** - * @file llmultifloater.h - * @brief LLFloater that hosts other floaters - * - * $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$ - */ - -// Floating "windows" within the GL display, like the inventory floater, -// mini-map floater, etc. - - -#ifndef LL_MULTI_FLOATER_H -#define LL_MULTI_FLOATER_H - -#include "llfloater.h" -#include "lltabcontainer.h" // for LLTabContainer::eInsertionPoint - -// https://wiki.lindenlab.com/mediawiki/index.php?title=LLMultiFloater&oldid=81376 -class LLMultiFloater : public LLFloater -{ -public: - LLMultiFloater(const LLSD& key, const Params& params = getDefaultParams()); - virtual ~LLMultiFloater() {}; - - void buildTabContainer(); - - virtual bool postBuild(); - /*virtual*/ void onClose(bool app_quitting); - virtual void draw(); - virtual void setVisible(bool visible); - /*virtual*/ bool handleKeyHere(KEY key, MASK mask); - /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); - - virtual void setCanResize(bool can_resize); - virtual void growToFit(S32 content_width, S32 content_height); - virtual void addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); - - virtual void showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); - virtual void removeFloater(LLFloater* floaterp); - - virtual void tabOpen(LLFloater* opened_floater, bool from_click); - virtual void tabClose(); - - virtual bool selectFloater(LLFloater* floaterp); - virtual void selectNextFloater(); - virtual void selectPrevFloater(); - - virtual LLFloater* getActiveFloater(); - virtual bool isFloaterFlashing(LLFloater* floaterp); - virtual S32 getFloaterCount(); - - virtual void setFloaterFlashing(LLFloater* floaterp, bool flashing); - virtual bool closeAllFloaters(); //Returns false if the floater could not be closed due to pending confirmation dialogs - void setTabContainer(LLTabContainer* tab_container) { if (!mTabContainer) mTabContainer = tab_container; } - void onTabSelected(); - - virtual void updateResizeLimits(); - virtual void updateFloaterTitle(LLFloater* floaterp); - -protected: - struct LLFloaterData - { - S32 mWidth; - S32 mHeight; - bool mCanMinimize; - bool mCanResize; - bool mSaveRect; - }; - - LLTabContainer* mTabContainer; - - typedef std::map, LLFloaterData> floater_data_map_t; - floater_data_map_t mFloaterDataMap; - - LLTabContainer::TabPosition mTabPos; - bool mAutoResize; - S32 mOrigMinWidth, mOrigMinHeight; // logically const but initialized late - -private: - virtual void computeResizeLimits(S32& new_min_width, S32& new_min_height); -}; - -#endif // LL_MULTI_FLOATER_H +/** + * @file llmultifloater.h + * @brief LLFloater that hosts other floaters + * + * $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$ + */ + +// Floating "windows" within the GL display, like the inventory floater, +// mini-map floater, etc. + + +#ifndef LL_MULTI_FLOATER_H +#define LL_MULTI_FLOATER_H + +#include "llfloater.h" +#include "lltabcontainer.h" // for LLTabContainer::eInsertionPoint + +// https://wiki.lindenlab.com/mediawiki/index.php?title=LLMultiFloater&oldid=81376 +class LLMultiFloater : public LLFloater +{ +public: + LLMultiFloater(const LLSD& key, const Params& params = getDefaultParams()); + virtual ~LLMultiFloater() {}; + + void buildTabContainer(); + + virtual bool postBuild(); + /*virtual*/ void onClose(bool app_quitting); + virtual void draw(); + virtual void setVisible(bool visible); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask); + /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); + + virtual void setCanResize(bool can_resize); + virtual void growToFit(S32 content_width, S32 content_height); + virtual void addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + + virtual void showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + virtual void removeFloater(LLFloater* floaterp); + + virtual void tabOpen(LLFloater* opened_floater, bool from_click); + virtual void tabClose(); + + virtual bool selectFloater(LLFloater* floaterp); + virtual void selectNextFloater(); + virtual void selectPrevFloater(); + + virtual LLFloater* getActiveFloater(); + virtual bool isFloaterFlashing(LLFloater* floaterp); + virtual S32 getFloaterCount(); + + virtual void setFloaterFlashing(LLFloater* floaterp, bool flashing); + virtual bool closeAllFloaters(); //Returns false if the floater could not be closed due to pending confirmation dialogs + void setTabContainer(LLTabContainer* tab_container) { if (!mTabContainer) mTabContainer = tab_container; } + void onTabSelected(); + + virtual void updateResizeLimits(); + virtual void updateFloaterTitle(LLFloater* floaterp); + +protected: + struct LLFloaterData + { + S32 mWidth; + S32 mHeight; + bool mCanMinimize; + bool mCanResize; + bool mSaveRect; + }; + + LLTabContainer* mTabContainer; + + typedef std::map, LLFloaterData> floater_data_map_t; + floater_data_map_t mFloaterDataMap; + + LLTabContainer::TabPosition mTabPos; + bool mAutoResize; + S32 mOrigMinWidth, mOrigMinHeight; // logically const but initialized late + +private: + virtual void computeResizeLimits(S32& new_min_width, S32& new_min_height); +}; + +#endif // LL_MULTI_FLOATER_H diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index a29ccab737..cfbf491610 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -1,880 +1,880 @@ -/** - * @file llmultisldr.cpp - * @brief LLMultiSlider base class - * - * $LicenseInfo:firstyear=2007&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 "llmultislider.h" -#include "llui.h" - -#include "llgl.h" -#include "llwindow.h" -#include "llfocusmgr.h" -#include "llkeyboard.h" // for the MASK constants -#include "llcontrol.h" -#include "lluictrlfactory.h" -#include "lluiimage.h" - -#include - -static LLDefaultChildRegistry::Register r("multi_slider_bar"); - -const F32 FLOAT_THRESHOLD = 0.00001f; - -S32 LLMultiSlider::mNameCounter = 0; - -LLMultiSlider::SliderParams::SliderParams() -: name("name"), - value("value", 0.f) -{ -} - -LLMultiSlider::Params::Params() -: max_sliders("max_sliders", 1), - allow_overlap("allow_overlap", false), - loop_overlap("loop_overlap", false), - orientation("orientation"), - overlap_threshold("overlap_threshold", 0), - draw_track("draw_track", true), - use_triangle("use_triangle", false), - track_color("track_color"), - thumb_disabled_color("thumb_disabled_color"), - thumb_highlight_color("thumb_highlight_color"), - thumb_outline_color("thumb_outline_color"), - thumb_center_color("thumb_center_color"), - thumb_center_selected_color("thumb_center_selected_color"), - thumb_image("thumb_image"), - triangle_color("triangle_color"), - mouse_down_callback("mouse_down_callback"), - mouse_up_callback("mouse_up_callback"), - thumb_width("thumb_width"), - sliders("slider") -{} - -LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p) -: LLF32UICtrl(p), - mMouseOffset( 0 ), - mMaxNumSliders(p.max_sliders), - mAllowOverlap(p.allow_overlap), - mLoopOverlap(p.loop_overlap), - mDrawTrack(p.draw_track), - mUseTriangle(p.use_triangle), - mTrackColor(p.track_color()), - mThumbOutlineColor(p.thumb_outline_color()), - mThumbCenterColor(p.thumb_center_color()), - mThumbCenterSelectedColor(p.thumb_center_selected_color()), - mDisabledThumbColor(p.thumb_disabled_color()), - mTriangleColor(p.triangle_color()), - mThumbWidth(p.thumb_width), - mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL) -{ - mValue = LLSD::emptyMap(); - mCurSlider = LLStringUtil::null; - - if (mOrientation == HORIZONTAL) - { - mDragStartThumbRect = LLRect(0, getRect().getHeight(), p.thumb_width, 0); - } - else - { - mDragStartThumbRect = LLRect(0, p.thumb_width, getRect().getWidth(), 0); - } - - if (p.mouse_down_callback.isProvided()) - { - setMouseDownCallback(initCommitCallback(p.mouse_down_callback)); - } - if (p.mouse_up_callback.isProvided()) - { - setMouseUpCallback(initCommitCallback(p.mouse_up_callback)); - } - - if (p.overlap_threshold.isProvided() && p.overlap_threshold > mIncrement) - { - mOverlapThreshold = p.overlap_threshold - mIncrement; - } - else - { - mOverlapThreshold = 0; - } - - for (LLInitParam::ParamIterator::const_iterator it = p.sliders.begin(); - it != p.sliders.end(); - ++it) - { - if (it->name.isProvided()) - { - addSlider(it->value, it->name); - } - else - { - addSlider(it->value); - } - } - - mRoundedSquareImgp = LLUI::getUIImage("Rounded_Square"); - if (p.thumb_image.isProvided()) - { - mThumbImagep = LLUI::getUIImage(p.thumb_image()); - } - mThumbHighlightColor = p.thumb_highlight_color.isProvided() ? p.thumb_highlight_color() : static_cast(gFocusMgr.getFocusColor()); -} - -LLMultiSlider::~LLMultiSlider() -{ - delete mMouseDownSignal; - delete mMouseUpSignal; -} - -F32 LLMultiSlider::getNearestIncrement(F32 value) const -{ - value = llclamp(value, mMinValue, mMaxValue); - - // Round to nearest increment (bias towards rounding down) - value -= mMinValue; - value += mIncrement / 2.0001f; - value -= fmod(value, mIncrement); - return mMinValue + value; -} - -void LLMultiSlider::setSliderValue(const std::string& name, F32 value, bool from_event) -{ - // exit if not there - if(!mValue.has(name)) { - return; - } - - F32 newValue = getNearestIncrement(value); - - // now, make sure no overlap - // if we want that - if(!mAllowOverlap) { - bool hit = false; - - // look at the current spot - // and see if anything is there - LLSD::map_iterator mIt = mValue.beginMap(); - - // increment is our distance between points, use to eliminate round error - F32 threshold = mOverlapThreshold + (mIncrement / 4); - // If loop overlap is enabled, check if we overlap with points 'after' max value (project to lower) - F32 loop_up_check = (mLoopOverlap && (value + threshold) > mMaxValue) ? (value + threshold - mMaxValue + mMinValue) : mMinValue - 1.0f; - // If loop overlap is enabled, check if we overlap with points 'before' min value (project to upper) - F32 loop_down_check = (mLoopOverlap && (value - threshold) < mMinValue) ? (value - threshold - mMinValue + mMaxValue) : mMaxValue + 1.0f; - - for(;mIt != mValue.endMap(); mIt++) - { - F32 locationVal = (F32)mIt->second.asReal(); - // Check nearby values - F32 testVal = locationVal - newValue; - if (testVal > -threshold - && testVal < threshold - && mIt->first != name) - { - hit = true; - break; - } - if (mLoopOverlap) - { - // Check edge overlap values - if (locationVal < loop_up_check) - { - hit = true; - break; - } - if (locationVal > loop_down_check) - { - hit = true; - break; - } - } - } - - // if none found, stop - if(hit) { - return; - } - } - - - // now set it in the map - mValue[name] = newValue; - - // set the control if it's the current slider and not from an event - if (!from_event && name == mCurSlider) - { - setControlValue(mValue); - } - - F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue); - if (mOrientation == HORIZONTAL) - { - S32 left_edge = mThumbWidth/2; - S32 right_edge = getRect().getWidth() - (mThumbWidth/2); - - S32 x = left_edge + S32( t * (right_edge - left_edge) ); - - mThumbRects[name].mLeft = x - (mThumbWidth / 2); - mThumbRects[name].mRight = x + (mThumbWidth / 2); - } - else - { - S32 bottom_edge = mThumbWidth/2; - S32 top_edge = getRect().getHeight() - (mThumbWidth/2); - - S32 x = bottom_edge + S32( t * (top_edge - bottom_edge) ); - - mThumbRects[name].mTop = x + (mThumbWidth / 2); - mThumbRects[name].mBottom = x - (mThumbWidth / 2); - } -} - -void LLMultiSlider::setValue(const LLSD& value) -{ - // only do if it's a map - if(value.isMap()) { - - // add each value... the first in the map becomes the current - LLSD::map_const_iterator mIt = value.beginMap(); - mCurSlider = mIt->first; - - for(; mIt != value.endMap(); mIt++) { - setSliderValue(mIt->first, (F32)mIt->second.asReal(), true); - } - } -} - -F32 LLMultiSlider::getSliderValue(const std::string& name) const -{ - if (mValue.has(name)) - { - return (F32)mValue[name].asReal(); - } - return 0; -} - -void LLMultiSlider::setCurSlider(const std::string& name) -{ - if(mValue.has(name)) { - mCurSlider = name; - } -} - -F32 LLMultiSlider::getSliderValueFromPos(S32 xpos, S32 ypos) const -{ - F32 t = 0; - if (mOrientation == HORIZONTAL) - { - S32 left_edge = mThumbWidth / 2; - S32 right_edge = getRect().getWidth() - (mThumbWidth / 2); - - xpos += mMouseOffset; - xpos = llclamp(xpos, left_edge, right_edge); - - t = F32(xpos - left_edge) / (right_edge - left_edge); - } - else - { - S32 bottom_edge = mThumbWidth / 2; - S32 top_edge = getRect().getHeight() - (mThumbWidth / 2); - - ypos += mMouseOffset; - ypos = llclamp(ypos, bottom_edge, top_edge); - - t = F32(ypos - bottom_edge) / (top_edge - bottom_edge); - } - - return((t * (mMaxValue - mMinValue)) + mMinValue); -} - - -LLRect LLMultiSlider::getSliderThumbRect(const std::string& name) const -{ - auto it = mThumbRects.find(name); - if (it != mThumbRects.end()) - return (*it).second; - return LLRect(); -} - -void LLMultiSlider::setSliderThumbImage(const std::string &name) -{ - if (!name.empty()) - { - mThumbImagep = LLUI::getUIImage(name); - } - else - clearSliderThumbImage(); -} - -void LLMultiSlider::clearSliderThumbImage() -{ - mThumbImagep = NULL; -} - -void LLMultiSlider::resetCurSlider() -{ - mCurSlider = LLStringUtil::null; -} - -const std::string& LLMultiSlider::addSlider() -{ - return addSlider(mInitialValue); -} - -const std::string& LLMultiSlider::addSlider(F32 val) -{ - std::stringstream newName; - F32 initVal = val; - - if(mValue.size() >= mMaxNumSliders) { - return LLStringUtil::null; - } - - // create a new name - newName << "sldr" << mNameCounter; - mNameCounter++; - - bool foundOne = findUnusedValue(initVal); - if(!foundOne) { - return LLStringUtil::null; - } - - // add a new thumb rect - if (mOrientation == HORIZONTAL) - { - mThumbRects[newName.str()] = LLRect(0, getRect().getHeight(), mThumbWidth, 0); - } - else - { - mThumbRects[newName.str()] = LLRect(0, mThumbWidth, getRect().getWidth(), 0); - } - - // add the value and set the current slider to this one - mValue.insert(newName.str(), initVal); - mCurSlider = newName.str(); - - // move the slider - setSliderValue(mCurSlider, initVal, true); - - return mCurSlider; -} - -bool LLMultiSlider::addSlider(F32 val, const std::string& name) -{ - F32 initVal = val; - - if(mValue.size() >= mMaxNumSliders) { - return false; - } - - bool foundOne = findUnusedValue(initVal); - if(!foundOne) { - return false; - } - - // add a new thumb rect - if (mOrientation == HORIZONTAL) - { - mThumbRects[name] = LLRect(0, getRect().getHeight(), mThumbWidth, 0); - } - else - { - mThumbRects[name] = LLRect(0, mThumbWidth, getRect().getWidth(), 0); - } - - // add the value and set the current slider to this one - mValue.insert(name, initVal); - mCurSlider = name; - - // move the slider - setSliderValue(mCurSlider, initVal, true); - - return true; -} - -bool LLMultiSlider::findUnusedValue(F32& initVal) -{ - bool firstTry = true; - - // find the first open slot starting with - // the initial value - while(true) { - - bool hit = false; - - // look at the current spot - // and see if anything is there - F32 threshold = mAllowOverlap ? FLOAT_THRESHOLD : mOverlapThreshold + (mIncrement / 4); - LLSD::map_iterator mIt = mValue.beginMap(); - for(;mIt != mValue.endMap(); mIt++) { - - F32 testVal = (F32)mIt->second.asReal() - initVal; - if(testVal > -threshold && testVal < threshold) - { - hit = true; - break; - } - } - - // if we found one - if(!hit) { - break; - } - - // increment and wrap if need be - initVal += mIncrement; - if(initVal > mMaxValue) { - initVal = mMinValue; - } - - // stop if it's filled - if(initVal == mInitialValue && !firstTry) { - LL_WARNS() << "Whoa! Too many multi slider elements to add one to" << LL_ENDL; - return false; - } - - firstTry = false; - continue; - } - - return true; -} - - -void LLMultiSlider::deleteSlider(const std::string& name) -{ - // can't delete last slider - if(mValue.size() <= 0) { - return; - } - - // get rid of value from mValue and its thumb rect - mValue.erase(name); - mThumbRects.erase(name); - - // set to the last created - if(mValue.size() > 0) { - std::map::iterator mIt = mThumbRects.end(); - mIt--; - mCurSlider = mIt->first; - } -} - -void LLMultiSlider::clear() -{ - while(mThumbRects.size() > 0 && mValue.size() > 0) { - deleteCurSlider(); - } - - if (mThumbRects.size() > 0 || mValue.size() > 0) - { - LL_WARNS() << "Failed to fully clear Multi slider" << LL_ENDL; - } - - LLF32UICtrl::clear(); -} - -bool LLMultiSlider::handleHover(S32 x, S32 y, MASK mask) -{ - if( gFocusMgr.getMouseCapture() == this ) - { - setCurSliderValue(getSliderValueFromPos(x, y)); - onCommit(); - - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - } - else - { - if (getEnabled()) - { - mHoverSlider.clear(); - std::map::iterator mIt = mThumbRects.begin(); - for (; mIt != mThumbRects.end(); mIt++) - { - if (mIt->second.pointInRect(x, y)) - { - mHoverSlider = mIt->first; - break; - } - } - } - else - { - mHoverSlider.clear(); - } - - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - } - return true; -} - -bool LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - if( gFocusMgr.getMouseCapture() == this ) - { - gFocusMgr.setMouseCapture( NULL ); - - if (mMouseUpSignal) - (*mMouseUpSignal)( this, LLSD() ); - - handled = true; - make_ui_sound("UISndClickRelease"); - } - else - { - handled = true; - } - - return handled; -} - -bool LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // only do sticky-focus on non-chrome widgets - if (!getIsChrome()) - { - setFocus(true); - } - if (mMouseDownSignal) - (*mMouseDownSignal)( this, LLSD() ); - - if (MASK_CONTROL & mask) // if CTRL is modifying - { - setCurSliderValue(mInitialValue); - onCommit(); - } - else - { - // scroll through thumbs to see if we have a new one selected and select that one - std::map::iterator mIt = mThumbRects.begin(); - for(; mIt != mThumbRects.end(); mIt++) { - - // check if inside. If so, set current slider and continue - if(mIt->second.pointInRect(x,y)) { - mCurSlider = mIt->first; - break; - } - } - - if (!mCurSlider.empty()) - { - // Find the offset of the actual mouse location from the center of the thumb. - if (mThumbRects[mCurSlider].pointInRect(x,y)) - { - if (mOrientation == HORIZONTAL) - { - mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth / 2) - x; - } - else - { - mMouseOffset = (mThumbRects[mCurSlider].mBottom + mThumbWidth / 2) - y; - } - } - else - { - mMouseOffset = 0; - } - - // Start dragging the thumb - // No handler needed for focus lost since this class has no state that depends on it. - gFocusMgr.setMouseCapture( this ); - mDragStartThumbRect = mThumbRects[mCurSlider]; - } - } - make_ui_sound("UISndClick"); - - return true; -} - -bool LLMultiSlider::handleKeyHere(KEY key, MASK mask) -{ - bool handled = false; - switch(key) - { - case KEY_UP: - case KEY_DOWN: - // eat up and down keys to be consistent - handled = true; - break; - case KEY_LEFT: - setCurSliderValue(getCurSliderValue() - getIncrement()); - onCommit(); - handled = true; - break; - case KEY_RIGHT: - setCurSliderValue(getCurSliderValue() + getIncrement()); - onCommit(); - handled = true; - break; - default: - break; - } - return handled; -} - -/*virtual*/ -void LLMultiSlider::onMouseLeave(S32 x, S32 y, MASK mask) -{ - mHoverSlider.clear(); - LLF32UICtrl::onMouseLeave(x, y, mask); -} - -void LLMultiSlider::draw() -{ - static LLUICachedControl extra_triangle_height ("UIExtraTriangleHeight", 0); - static LLUICachedControl extra_triangle_width ("UIExtraTriangleWidth", 0); - LLColor4 curThumbColor; - - std::map::iterator mIt; - std::map::iterator curSldrIt; - std::map::iterator hoverSldrIt; - - // Draw background and thumb. - - // drawing solids requires texturing be disabled - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLRect rect(mDragStartThumbRect); - - F32 opacity = getEnabled() ? 1.f : 0.3f; - - // Track - static LLUICachedControl multi_track_height_width ("UIMultiTrackHeight", 0); - S32 height_offset = 0; - S32 width_offset = 0; - if (mOrientation == HORIZONTAL) - { - height_offset = (getRect().getHeight() - multi_track_height_width) / 2; - } - else - { - width_offset = (getRect().getWidth() - multi_track_height_width) / 2; - } - LLRect track_rect(width_offset, getRect().getHeight() - height_offset, getRect().getWidth() - width_offset, height_offset); - - - if(mDrawTrack) - { - track_rect.stretch(-1); - mRoundedSquareImgp->draw(track_rect, mTrackColor.get() % opacity); - } - - // if we're supposed to use a drawn triangle - // simple gl call for the triangle - if(mUseTriangle) { - - for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) { - - gl_triangle_2d( - mIt->second.mLeft - extra_triangle_width, - mIt->second.mTop + extra_triangle_height, - mIt->second.mRight + extra_triangle_width, - mIt->second.mTop + extra_triangle_height, - mIt->second.mLeft + mIt->second.getWidth() / 2, - mIt->second.mBottom - extra_triangle_height, - mTriangleColor.get() % opacity, true); - } - } - else if (!mRoundedSquareImgp && !mThumbImagep) - { - // draw all the thumbs - curSldrIt = mThumbRects.end(); - hoverSldrIt = mThumbRects.end(); - for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) { - - // choose the color - curThumbColor = mThumbCenterColor.get(); - if(mIt->first == mCurSlider) { - - curSldrIt = mIt; - continue; - } - if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this) - { - // draw last, after current one - hoverSldrIt = mIt; - continue; - } - - // the draw command - gl_rect_2d(mIt->second, curThumbColor, true); - } - - // now draw the current and hover sliders - if(curSldrIt != mThumbRects.end()) - { - gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), true); - } - - // and draw the drag start - if (gFocusMgr.getMouseCapture() == this) - { - gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, false); - } - else if (hoverSldrIt != mThumbRects.end()) - { - gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), true); - } - } - else - { - LLMouseHandler* capture = gFocusMgr.getMouseCapture(); - if (capture == this) - { - // draw drag start (ghost) - if (mThumbImagep) - { - mThumbImagep->draw(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f); - } - else - { - mRoundedSquareImgp->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f); - } - } - - // draw the highlight - if (hasFocus()) - { - if (!mCurSlider.empty()) - { - if (mThumbImagep) - { - mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth()); - } - else - { - mRoundedSquareImgp->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth()); - } - } - } - if (!mHoverSlider.empty()) - { - if (mThumbImagep) - { - mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth()); - } - else - { - mRoundedSquareImgp->drawBorder(mThumbRects[mHoverSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth()); - } - } - - // draw the thumbs - curSldrIt = mThumbRects.end(); - hoverSldrIt = mThumbRects.end(); - for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) - { - // choose the color - curThumbColor = mThumbCenterColor.get(); - if(mIt->first == mCurSlider) - { - // don't draw now, draw last - curSldrIt = mIt; - continue; - } - if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this) - { - // don't draw now, draw last, after current one - hoverSldrIt = mIt; - continue; - } - - // the draw command - if (mThumbImagep) - { - if (getEnabled()) - { - mThumbImagep->draw(mIt->second); - } - else - { - mThumbImagep->draw(mIt->second, LLColor4::grey % 0.8f); - } - } - else if (capture == this) - { - mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor); - } - else - { - mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor % opacity); - } - } - - // draw cur and hover slider last - if(curSldrIt != mThumbRects.end()) - { - if (mThumbImagep) - { - if (getEnabled()) - { - mThumbImagep->draw(curSldrIt->second); - } - else - { - mThumbImagep->draw(curSldrIt->second, LLColor4::grey % 0.8f); - } - } - else if (capture == this) - { - mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get()); - } - else - { - mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity); - } - } - if(hoverSldrIt != mThumbRects.end()) - { - if (mThumbImagep) - { - mThumbImagep->draw(hoverSldrIt->second); - } - else - { - mRoundedSquareImgp->drawSolid(hoverSldrIt->second, mThumbCenterSelectedColor.get()); - } - } - } - - LLF32UICtrl::draw(); -} -boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t(); - return mMouseDownSignal->connect(cb); -} - -boost::signals2::connection LLMultiSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t(); - return mMouseUpSignal->connect(cb); -} +/** + * @file llmultisldr.cpp + * @brief LLMultiSlider base class + * + * $LicenseInfo:firstyear=2007&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 "llmultislider.h" +#include "llui.h" + +#include "llgl.h" +#include "llwindow.h" +#include "llfocusmgr.h" +#include "llkeyboard.h" // for the MASK constants +#include "llcontrol.h" +#include "lluictrlfactory.h" +#include "lluiimage.h" + +#include + +static LLDefaultChildRegistry::Register r("multi_slider_bar"); + +const F32 FLOAT_THRESHOLD = 0.00001f; + +S32 LLMultiSlider::mNameCounter = 0; + +LLMultiSlider::SliderParams::SliderParams() +: name("name"), + value("value", 0.f) +{ +} + +LLMultiSlider::Params::Params() +: max_sliders("max_sliders", 1), + allow_overlap("allow_overlap", false), + loop_overlap("loop_overlap", false), + orientation("orientation"), + overlap_threshold("overlap_threshold", 0), + draw_track("draw_track", true), + use_triangle("use_triangle", false), + track_color("track_color"), + thumb_disabled_color("thumb_disabled_color"), + thumb_highlight_color("thumb_highlight_color"), + thumb_outline_color("thumb_outline_color"), + thumb_center_color("thumb_center_color"), + thumb_center_selected_color("thumb_center_selected_color"), + thumb_image("thumb_image"), + triangle_color("triangle_color"), + mouse_down_callback("mouse_down_callback"), + mouse_up_callback("mouse_up_callback"), + thumb_width("thumb_width"), + sliders("slider") +{} + +LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p) +: LLF32UICtrl(p), + mMouseOffset( 0 ), + mMaxNumSliders(p.max_sliders), + mAllowOverlap(p.allow_overlap), + mLoopOverlap(p.loop_overlap), + mDrawTrack(p.draw_track), + mUseTriangle(p.use_triangle), + mTrackColor(p.track_color()), + mThumbOutlineColor(p.thumb_outline_color()), + mThumbCenterColor(p.thumb_center_color()), + mThumbCenterSelectedColor(p.thumb_center_selected_color()), + mDisabledThumbColor(p.thumb_disabled_color()), + mTriangleColor(p.triangle_color()), + mThumbWidth(p.thumb_width), + mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL), + mMouseDownSignal(NULL), + mMouseUpSignal(NULL) +{ + mValue = LLSD::emptyMap(); + mCurSlider = LLStringUtil::null; + + if (mOrientation == HORIZONTAL) + { + mDragStartThumbRect = LLRect(0, getRect().getHeight(), p.thumb_width, 0); + } + else + { + mDragStartThumbRect = LLRect(0, p.thumb_width, getRect().getWidth(), 0); + } + + if (p.mouse_down_callback.isProvided()) + { + setMouseDownCallback(initCommitCallback(p.mouse_down_callback)); + } + if (p.mouse_up_callback.isProvided()) + { + setMouseUpCallback(initCommitCallback(p.mouse_up_callback)); + } + + if (p.overlap_threshold.isProvided() && p.overlap_threshold > mIncrement) + { + mOverlapThreshold = p.overlap_threshold - mIncrement; + } + else + { + mOverlapThreshold = 0; + } + + for (LLInitParam::ParamIterator::const_iterator it = p.sliders.begin(); + it != p.sliders.end(); + ++it) + { + if (it->name.isProvided()) + { + addSlider(it->value, it->name); + } + else + { + addSlider(it->value); + } + } + + mRoundedSquareImgp = LLUI::getUIImage("Rounded_Square"); + if (p.thumb_image.isProvided()) + { + mThumbImagep = LLUI::getUIImage(p.thumb_image()); + } + mThumbHighlightColor = p.thumb_highlight_color.isProvided() ? p.thumb_highlight_color() : static_cast(gFocusMgr.getFocusColor()); +} + +LLMultiSlider::~LLMultiSlider() +{ + delete mMouseDownSignal; + delete mMouseUpSignal; +} + +F32 LLMultiSlider::getNearestIncrement(F32 value) const +{ + value = llclamp(value, mMinValue, mMaxValue); + + // Round to nearest increment (bias towards rounding down) + value -= mMinValue; + value += mIncrement / 2.0001f; + value -= fmod(value, mIncrement); + return mMinValue + value; +} + +void LLMultiSlider::setSliderValue(const std::string& name, F32 value, bool from_event) +{ + // exit if not there + if(!mValue.has(name)) { + return; + } + + F32 newValue = getNearestIncrement(value); + + // now, make sure no overlap + // if we want that + if(!mAllowOverlap) { + bool hit = false; + + // look at the current spot + // and see if anything is there + LLSD::map_iterator mIt = mValue.beginMap(); + + // increment is our distance between points, use to eliminate round error + F32 threshold = mOverlapThreshold + (mIncrement / 4); + // If loop overlap is enabled, check if we overlap with points 'after' max value (project to lower) + F32 loop_up_check = (mLoopOverlap && (value + threshold) > mMaxValue) ? (value + threshold - mMaxValue + mMinValue) : mMinValue - 1.0f; + // If loop overlap is enabled, check if we overlap with points 'before' min value (project to upper) + F32 loop_down_check = (mLoopOverlap && (value - threshold) < mMinValue) ? (value - threshold - mMinValue + mMaxValue) : mMaxValue + 1.0f; + + for(;mIt != mValue.endMap(); mIt++) + { + F32 locationVal = (F32)mIt->second.asReal(); + // Check nearby values + F32 testVal = locationVal - newValue; + if (testVal > -threshold + && testVal < threshold + && mIt->first != name) + { + hit = true; + break; + } + if (mLoopOverlap) + { + // Check edge overlap values + if (locationVal < loop_up_check) + { + hit = true; + break; + } + if (locationVal > loop_down_check) + { + hit = true; + break; + } + } + } + + // if none found, stop + if(hit) { + return; + } + } + + + // now set it in the map + mValue[name] = newValue; + + // set the control if it's the current slider and not from an event + if (!from_event && name == mCurSlider) + { + setControlValue(mValue); + } + + F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue); + if (mOrientation == HORIZONTAL) + { + S32 left_edge = mThumbWidth/2; + S32 right_edge = getRect().getWidth() - (mThumbWidth/2); + + S32 x = left_edge + S32( t * (right_edge - left_edge) ); + + mThumbRects[name].mLeft = x - (mThumbWidth / 2); + mThumbRects[name].mRight = x + (mThumbWidth / 2); + } + else + { + S32 bottom_edge = mThumbWidth/2; + S32 top_edge = getRect().getHeight() - (mThumbWidth/2); + + S32 x = bottom_edge + S32( t * (top_edge - bottom_edge) ); + + mThumbRects[name].mTop = x + (mThumbWidth / 2); + mThumbRects[name].mBottom = x - (mThumbWidth / 2); + } +} + +void LLMultiSlider::setValue(const LLSD& value) +{ + // only do if it's a map + if(value.isMap()) { + + // add each value... the first in the map becomes the current + LLSD::map_const_iterator mIt = value.beginMap(); + mCurSlider = mIt->first; + + for(; mIt != value.endMap(); mIt++) { + setSliderValue(mIt->first, (F32)mIt->second.asReal(), true); + } + } +} + +F32 LLMultiSlider::getSliderValue(const std::string& name) const +{ + if (mValue.has(name)) + { + return (F32)mValue[name].asReal(); + } + return 0; +} + +void LLMultiSlider::setCurSlider(const std::string& name) +{ + if(mValue.has(name)) { + mCurSlider = name; + } +} + +F32 LLMultiSlider::getSliderValueFromPos(S32 xpos, S32 ypos) const +{ + F32 t = 0; + if (mOrientation == HORIZONTAL) + { + S32 left_edge = mThumbWidth / 2; + S32 right_edge = getRect().getWidth() - (mThumbWidth / 2); + + xpos += mMouseOffset; + xpos = llclamp(xpos, left_edge, right_edge); + + t = F32(xpos - left_edge) / (right_edge - left_edge); + } + else + { + S32 bottom_edge = mThumbWidth / 2; + S32 top_edge = getRect().getHeight() - (mThumbWidth / 2); + + ypos += mMouseOffset; + ypos = llclamp(ypos, bottom_edge, top_edge); + + t = F32(ypos - bottom_edge) / (top_edge - bottom_edge); + } + + return((t * (mMaxValue - mMinValue)) + mMinValue); +} + + +LLRect LLMultiSlider::getSliderThumbRect(const std::string& name) const +{ + auto it = mThumbRects.find(name); + if (it != mThumbRects.end()) + return (*it).second; + return LLRect(); +} + +void LLMultiSlider::setSliderThumbImage(const std::string &name) +{ + if (!name.empty()) + { + mThumbImagep = LLUI::getUIImage(name); + } + else + clearSliderThumbImage(); +} + +void LLMultiSlider::clearSliderThumbImage() +{ + mThumbImagep = NULL; +} + +void LLMultiSlider::resetCurSlider() +{ + mCurSlider = LLStringUtil::null; +} + +const std::string& LLMultiSlider::addSlider() +{ + return addSlider(mInitialValue); +} + +const std::string& LLMultiSlider::addSlider(F32 val) +{ + std::stringstream newName; + F32 initVal = val; + + if(mValue.size() >= mMaxNumSliders) { + return LLStringUtil::null; + } + + // create a new name + newName << "sldr" << mNameCounter; + mNameCounter++; + + bool foundOne = findUnusedValue(initVal); + if(!foundOne) { + return LLStringUtil::null; + } + + // add a new thumb rect + if (mOrientation == HORIZONTAL) + { + mThumbRects[newName.str()] = LLRect(0, getRect().getHeight(), mThumbWidth, 0); + } + else + { + mThumbRects[newName.str()] = LLRect(0, mThumbWidth, getRect().getWidth(), 0); + } + + // add the value and set the current slider to this one + mValue.insert(newName.str(), initVal); + mCurSlider = newName.str(); + + // move the slider + setSliderValue(mCurSlider, initVal, true); + + return mCurSlider; +} + +bool LLMultiSlider::addSlider(F32 val, const std::string& name) +{ + F32 initVal = val; + + if(mValue.size() >= mMaxNumSliders) { + return false; + } + + bool foundOne = findUnusedValue(initVal); + if(!foundOne) { + return false; + } + + // add a new thumb rect + if (mOrientation == HORIZONTAL) + { + mThumbRects[name] = LLRect(0, getRect().getHeight(), mThumbWidth, 0); + } + else + { + mThumbRects[name] = LLRect(0, mThumbWidth, getRect().getWidth(), 0); + } + + // add the value and set the current slider to this one + mValue.insert(name, initVal); + mCurSlider = name; + + // move the slider + setSliderValue(mCurSlider, initVal, true); + + return true; +} + +bool LLMultiSlider::findUnusedValue(F32& initVal) +{ + bool firstTry = true; + + // find the first open slot starting with + // the initial value + while(true) { + + bool hit = false; + + // look at the current spot + // and see if anything is there + F32 threshold = mAllowOverlap ? FLOAT_THRESHOLD : mOverlapThreshold + (mIncrement / 4); + LLSD::map_iterator mIt = mValue.beginMap(); + for(;mIt != mValue.endMap(); mIt++) { + + F32 testVal = (F32)mIt->second.asReal() - initVal; + if(testVal > -threshold && testVal < threshold) + { + hit = true; + break; + } + } + + // if we found one + if(!hit) { + break; + } + + // increment and wrap if need be + initVal += mIncrement; + if(initVal > mMaxValue) { + initVal = mMinValue; + } + + // stop if it's filled + if(initVal == mInitialValue && !firstTry) { + LL_WARNS() << "Whoa! Too many multi slider elements to add one to" << LL_ENDL; + return false; + } + + firstTry = false; + continue; + } + + return true; +} + + +void LLMultiSlider::deleteSlider(const std::string& name) +{ + // can't delete last slider + if(mValue.size() <= 0) { + return; + } + + // get rid of value from mValue and its thumb rect + mValue.erase(name); + mThumbRects.erase(name); + + // set to the last created + if(mValue.size() > 0) { + std::map::iterator mIt = mThumbRects.end(); + mIt--; + mCurSlider = mIt->first; + } +} + +void LLMultiSlider::clear() +{ + while(mThumbRects.size() > 0 && mValue.size() > 0) { + deleteCurSlider(); + } + + if (mThumbRects.size() > 0 || mValue.size() > 0) + { + LL_WARNS() << "Failed to fully clear Multi slider" << LL_ENDL; + } + + LLF32UICtrl::clear(); +} + +bool LLMultiSlider::handleHover(S32 x, S32 y, MASK mask) +{ + if( gFocusMgr.getMouseCapture() == this ) + { + setCurSliderValue(getSliderValueFromPos(x, y)); + onCommit(); + + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; + } + else + { + if (getEnabled()) + { + mHoverSlider.clear(); + std::map::iterator mIt = mThumbRects.begin(); + for (; mIt != mThumbRects.end(); mIt++) + { + if (mIt->second.pointInRect(x, y)) + { + mHoverSlider = mIt->first; + break; + } + } + } + else + { + mHoverSlider.clear(); + } + + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; + } + return true; +} + +bool LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if( gFocusMgr.getMouseCapture() == this ) + { + gFocusMgr.setMouseCapture( NULL ); + + if (mMouseUpSignal) + (*mMouseUpSignal)( this, LLSD() ); + + handled = true; + make_ui_sound("UISndClickRelease"); + } + else + { + handled = true; + } + + return handled; +} + +bool LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // only do sticky-focus on non-chrome widgets + if (!getIsChrome()) + { + setFocus(true); + } + if (mMouseDownSignal) + (*mMouseDownSignal)( this, LLSD() ); + + if (MASK_CONTROL & mask) // if CTRL is modifying + { + setCurSliderValue(mInitialValue); + onCommit(); + } + else + { + // scroll through thumbs to see if we have a new one selected and select that one + std::map::iterator mIt = mThumbRects.begin(); + for(; mIt != mThumbRects.end(); mIt++) { + + // check if inside. If so, set current slider and continue + if(mIt->second.pointInRect(x,y)) { + mCurSlider = mIt->first; + break; + } + } + + if (!mCurSlider.empty()) + { + // Find the offset of the actual mouse location from the center of the thumb. + if (mThumbRects[mCurSlider].pointInRect(x,y)) + { + if (mOrientation == HORIZONTAL) + { + mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth / 2) - x; + } + else + { + mMouseOffset = (mThumbRects[mCurSlider].mBottom + mThumbWidth / 2) - y; + } + } + else + { + mMouseOffset = 0; + } + + // Start dragging the thumb + // No handler needed for focus lost since this class has no state that depends on it. + gFocusMgr.setMouseCapture( this ); + mDragStartThumbRect = mThumbRects[mCurSlider]; + } + } + make_ui_sound("UISndClick"); + + return true; +} + +bool LLMultiSlider::handleKeyHere(KEY key, MASK mask) +{ + bool handled = false; + switch(key) + { + case KEY_UP: + case KEY_DOWN: + // eat up and down keys to be consistent + handled = true; + break; + case KEY_LEFT: + setCurSliderValue(getCurSliderValue() - getIncrement()); + onCommit(); + handled = true; + break; + case KEY_RIGHT: + setCurSliderValue(getCurSliderValue() + getIncrement()); + onCommit(); + handled = true; + break; + default: + break; + } + return handled; +} + +/*virtual*/ +void LLMultiSlider::onMouseLeave(S32 x, S32 y, MASK mask) +{ + mHoverSlider.clear(); + LLF32UICtrl::onMouseLeave(x, y, mask); +} + +void LLMultiSlider::draw() +{ + static LLUICachedControl extra_triangle_height ("UIExtraTriangleHeight", 0); + static LLUICachedControl extra_triangle_width ("UIExtraTriangleWidth", 0); + LLColor4 curThumbColor; + + std::map::iterator mIt; + std::map::iterator curSldrIt; + std::map::iterator hoverSldrIt; + + // Draw background and thumb. + + // drawing solids requires texturing be disabled + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLRect rect(mDragStartThumbRect); + + F32 opacity = getEnabled() ? 1.f : 0.3f; + + // Track + static LLUICachedControl multi_track_height_width ("UIMultiTrackHeight", 0); + S32 height_offset = 0; + S32 width_offset = 0; + if (mOrientation == HORIZONTAL) + { + height_offset = (getRect().getHeight() - multi_track_height_width) / 2; + } + else + { + width_offset = (getRect().getWidth() - multi_track_height_width) / 2; + } + LLRect track_rect(width_offset, getRect().getHeight() - height_offset, getRect().getWidth() - width_offset, height_offset); + + + if(mDrawTrack) + { + track_rect.stretch(-1); + mRoundedSquareImgp->draw(track_rect, mTrackColor.get() % opacity); + } + + // if we're supposed to use a drawn triangle + // simple gl call for the triangle + if(mUseTriangle) { + + for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) { + + gl_triangle_2d( + mIt->second.mLeft - extra_triangle_width, + mIt->second.mTop + extra_triangle_height, + mIt->second.mRight + extra_triangle_width, + mIt->second.mTop + extra_triangle_height, + mIt->second.mLeft + mIt->second.getWidth() / 2, + mIt->second.mBottom - extra_triangle_height, + mTriangleColor.get() % opacity, true); + } + } + else if (!mRoundedSquareImgp && !mThumbImagep) + { + // draw all the thumbs + curSldrIt = mThumbRects.end(); + hoverSldrIt = mThumbRects.end(); + for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) { + + // choose the color + curThumbColor = mThumbCenterColor.get(); + if(mIt->first == mCurSlider) { + + curSldrIt = mIt; + continue; + } + if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this) + { + // draw last, after current one + hoverSldrIt = mIt; + continue; + } + + // the draw command + gl_rect_2d(mIt->second, curThumbColor, true); + } + + // now draw the current and hover sliders + if(curSldrIt != mThumbRects.end()) + { + gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), true); + } + + // and draw the drag start + if (gFocusMgr.getMouseCapture() == this) + { + gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, false); + } + else if (hoverSldrIt != mThumbRects.end()) + { + gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), true); + } + } + else + { + LLMouseHandler* capture = gFocusMgr.getMouseCapture(); + if (capture == this) + { + // draw drag start (ghost) + if (mThumbImagep) + { + mThumbImagep->draw(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f); + } + else + { + mRoundedSquareImgp->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f); + } + } + + // draw the highlight + if (hasFocus()) + { + if (!mCurSlider.empty()) + { + if (mThumbImagep) + { + mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth()); + } + else + { + mRoundedSquareImgp->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth()); + } + } + } + if (!mHoverSlider.empty()) + { + if (mThumbImagep) + { + mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth()); + } + else + { + mRoundedSquareImgp->drawBorder(mThumbRects[mHoverSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth()); + } + } + + // draw the thumbs + curSldrIt = mThumbRects.end(); + hoverSldrIt = mThumbRects.end(); + for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) + { + // choose the color + curThumbColor = mThumbCenterColor.get(); + if(mIt->first == mCurSlider) + { + // don't draw now, draw last + curSldrIt = mIt; + continue; + } + if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this) + { + // don't draw now, draw last, after current one + hoverSldrIt = mIt; + continue; + } + + // the draw command + if (mThumbImagep) + { + if (getEnabled()) + { + mThumbImagep->draw(mIt->second); + } + else + { + mThumbImagep->draw(mIt->second, LLColor4::grey % 0.8f); + } + } + else if (capture == this) + { + mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor); + } + else + { + mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor % opacity); + } + } + + // draw cur and hover slider last + if(curSldrIt != mThumbRects.end()) + { + if (mThumbImagep) + { + if (getEnabled()) + { + mThumbImagep->draw(curSldrIt->second); + } + else + { + mThumbImagep->draw(curSldrIt->second, LLColor4::grey % 0.8f); + } + } + else if (capture == this) + { + mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get()); + } + else + { + mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity); + } + } + if(hoverSldrIt != mThumbRects.end()) + { + if (mThumbImagep) + { + mThumbImagep->draw(hoverSldrIt->second); + } + else + { + mRoundedSquareImgp->drawSolid(hoverSldrIt->second, mThumbCenterSelectedColor.get()); + } + } + } + + LLF32UICtrl::draw(); +} +boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t(); + return mMouseDownSignal->connect(cb); +} + +boost::signals2::connection LLMultiSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t(); + return mMouseUpSignal->connect(cb); +} diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h index e0ac563e75..630e45dddb 100644 --- a/indra/llui/llmultislider.h +++ b/indra/llui/llmultislider.h @@ -1,161 +1,161 @@ -/** - * @file llmultislider.h - * @brief A simple multislider - * - * $LicenseInfo:firstyear=2007&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_MULTI_SLIDER_H -#define LL_MULTI_SLIDER_H - -#include "llf32uictrl.h" -#include "v4color.h" - -class LLUICtrlFactory; - -class LLMultiSlider : public LLF32UICtrl -{ -public: - struct SliderParams : public LLInitParam::Block - { - Optional name; - Mandatory value; - SliderParams(); - }; - - struct Params : public LLInitParam::Block - { - Optional max_sliders; - - Optional allow_overlap, - loop_overlap, - draw_track, - use_triangle; - - Optional overlap_threshold; - - Optional track_color, - thumb_disabled_color, - thumb_highlight_color, - thumb_outline_color, - thumb_center_color, - thumb_center_selected_color, - triangle_color; - - Optional orientation, - thumb_image; - - Optional mouse_down_callback, - mouse_up_callback; - Optional thumb_width; - - Multiple sliders; - Params(); - }; - -protected: - LLMultiSlider(const Params&); - friend class LLUICtrlFactory; -public: - virtual ~LLMultiSlider(); - - // Multi-slider rounds values to nearest increments (bias towards rounding down) - F32 getNearestIncrement(F32 value) const; - - void setSliderValue(const std::string& name, F32 value, bool from_event = false); - F32 getSliderValue(const std::string& name) const; - F32 getSliderValueFromPos(S32 xpos, S32 ypos) const; - LLRect getSliderThumbRect(const std::string& name) const; - - void setSliderThumbImage(const std::string &name); - void clearSliderThumbImage(); - - - const std::string& getCurSlider() const { return mCurSlider; } - F32 getCurSliderValue() const { return getSliderValue(mCurSlider); } - void setCurSlider(const std::string& name); - void resetCurSlider(); - void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mCurSlider, val, from_event); } - - /*virtual*/ void setValue(const LLSD& value) override; - /*virtual*/ LLSD getValue() const override { return mValue; } - - boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); - - bool findUnusedValue(F32& initVal); - const std::string& addSlider(); - const std::string& addSlider(F32 val); - bool addSlider(F32 val, const std::string& name); - void deleteSlider(const std::string& name); - void deleteCurSlider() { deleteSlider(mCurSlider); } - /*virtual*/ void clear() override; - - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override; - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; - /*virtual*/ void draw() override; - - S32 getMaxNumSliders() { return mMaxNumSliders; } - S32 getCurNumSliders() { return mValue.size(); } - F32 getOverlapThreshold() { return mOverlapThreshold; } - bool canAddSliders() { return mValue.size() < mMaxNumSliders; } - - -protected: - LLSD mValue; - std::string mCurSlider; - std::string mHoverSlider; - static S32 mNameCounter; - - S32 mMaxNumSliders; - bool mAllowOverlap; - bool mLoopOverlap; - F32 mOverlapThreshold; - bool mDrawTrack; - bool mUseTriangle; /// hacked in toggle to use a triangle - - S32 mMouseOffset; - LLRect mDragStartThumbRect; - S32 mThumbWidth; - - std::map - mThumbRects; - LLUIColor mTrackColor; - LLUIColor mThumbOutlineColor; - LLUIColor mThumbHighlightColor; - LLUIColor mThumbCenterColor; - LLUIColor mThumbCenterSelectedColor; - LLUIColor mDisabledThumbColor; - LLUIColor mTriangleColor; - LLUIImagePtr mThumbImagep; //blimps on the slider, for now no 'disabled' support - LLUIImagePtr mRoundedSquareImgp; //blimps on the slider, for now no 'disabled' support - - const EOrientation mOrientation; - - commit_signal_t* mMouseDownSignal; - commit_signal_t* mMouseUpSignal; -}; - -#endif // LL_MULTI_SLIDER_H +/** + * @file llmultislider.h + * @brief A simple multislider + * + * $LicenseInfo:firstyear=2007&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_MULTI_SLIDER_H +#define LL_MULTI_SLIDER_H + +#include "llf32uictrl.h" +#include "v4color.h" + +class LLUICtrlFactory; + +class LLMultiSlider : public LLF32UICtrl +{ +public: + struct SliderParams : public LLInitParam::Block + { + Optional name; + Mandatory value; + SliderParams(); + }; + + struct Params : public LLInitParam::Block + { + Optional max_sliders; + + Optional allow_overlap, + loop_overlap, + draw_track, + use_triangle; + + Optional overlap_threshold; + + Optional track_color, + thumb_disabled_color, + thumb_highlight_color, + thumb_outline_color, + thumb_center_color, + thumb_center_selected_color, + triangle_color; + + Optional orientation, + thumb_image; + + Optional mouse_down_callback, + mouse_up_callback; + Optional thumb_width; + + Multiple sliders; + Params(); + }; + +protected: + LLMultiSlider(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLMultiSlider(); + + // Multi-slider rounds values to nearest increments (bias towards rounding down) + F32 getNearestIncrement(F32 value) const; + + void setSliderValue(const std::string& name, F32 value, bool from_event = false); + F32 getSliderValue(const std::string& name) const; + F32 getSliderValueFromPos(S32 xpos, S32 ypos) const; + LLRect getSliderThumbRect(const std::string& name) const; + + void setSliderThumbImage(const std::string &name); + void clearSliderThumbImage(); + + + const std::string& getCurSlider() const { return mCurSlider; } + F32 getCurSliderValue() const { return getSliderValue(mCurSlider); } + void setCurSlider(const std::string& name); + void resetCurSlider(); + void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mCurSlider, val, from_event); } + + /*virtual*/ void setValue(const LLSD& value) override; + /*virtual*/ LLSD getValue() const override { return mValue; } + + boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); + + bool findUnusedValue(F32& initVal); + const std::string& addSlider(); + const std::string& addSlider(F32 val); + bool addSlider(F32 val, const std::string& name); + void deleteSlider(const std::string& name); + void deleteCurSlider() { deleteSlider(mCurSlider); } + /*virtual*/ void clear() override; + + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override; + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; + /*virtual*/ void draw() override; + + S32 getMaxNumSliders() { return mMaxNumSliders; } + S32 getCurNumSliders() { return mValue.size(); } + F32 getOverlapThreshold() { return mOverlapThreshold; } + bool canAddSliders() { return mValue.size() < mMaxNumSliders; } + + +protected: + LLSD mValue; + std::string mCurSlider; + std::string mHoverSlider; + static S32 mNameCounter; + + S32 mMaxNumSliders; + bool mAllowOverlap; + bool mLoopOverlap; + F32 mOverlapThreshold; + bool mDrawTrack; + bool mUseTriangle; /// hacked in toggle to use a triangle + + S32 mMouseOffset; + LLRect mDragStartThumbRect; + S32 mThumbWidth; + + std::map + mThumbRects; + LLUIColor mTrackColor; + LLUIColor mThumbOutlineColor; + LLUIColor mThumbHighlightColor; + LLUIColor mThumbCenterColor; + LLUIColor mThumbCenterSelectedColor; + LLUIColor mDisabledThumbColor; + LLUIColor mTriangleColor; + LLUIImagePtr mThumbImagep; //blimps on the slider, for now no 'disabled' support + LLUIImagePtr mRoundedSquareImgp; //blimps on the slider, for now no 'disabled' support + + const EOrientation mOrientation; + + commit_signal_t* mMouseDownSignal; + commit_signal_t* mMouseUpSignal; +}; + +#endif // LL_MULTI_SLIDER_H diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index 879fdde254..01e2fc6ac9 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -1,525 +1,525 @@ -/** - * @file llmultisliderctrl.cpp - * @brief LLMultiSliderCtrl base class - * - * $LicenseInfo:firstyear=2007&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 "llmultisliderctrl.h" - -#include "llmath.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llmultislider.h" -#include "llstring.h" -#include "lltextbox.h" -#include "llui.h" -#include "lluiconstants.h" -#include "llcontrol.h" -#include "llfocusmgr.h" -#include "llresmgr.h" -#include "lluictrlfactory.h" - -static LLDefaultChildRegistry::Register r("multi_slider"); - -const U32 MAX_STRING_LENGTH = 10; -LLMultiSliderCtrl::Params::Params() -: text_width("text_width"), - label_width("label_width"), - show_text("show_text", true), - can_edit_text("can_edit_text", false), - max_sliders("max_sliders", 1), - allow_overlap("allow_overlap", false), - loop_overlap("loop_overlap", false), - orientation("orientation"), - thumb_image("thumb_image"), - thumb_width("thumb_width"), - thumb_highlight_color("thumb_highlight_color"), - overlap_threshold("overlap_threshold", 0), - draw_track("draw_track", true), - use_triangle("use_triangle", false), - decimal_digits("decimal_digits", 3), - text_color("text_color"), - text_disabled_color("text_disabled_color"), - mouse_down_callback("mouse_down_callback"), - mouse_up_callback("mouse_up_callback"), - sliders("slider") -{ - mouse_opaque = true; -} - -LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p) -: LLF32UICtrl(p), - mLabelBox( NULL ), - mEditor( NULL ), - mTextBox( NULL ), - mTextEnabledColor(p.text_color()), - mTextDisabledColor(p.text_disabled_color()) -{ - static LLUICachedControl multi_sliderctrl_spacing ("UIMultiSliderctrlSpacing", 0); - - S32 top = getRect().getHeight(); - S32 bottom = 0; - S32 left = 0; - - S32 label_width = p.label_width; - S32 text_width = p.text_width; - - // Label - if( !p.label().empty() ) - { - if (p.label_width == 0) - { - label_width = p.font()->getWidth(p.label); - } - LLRect label_rect( left, top, label_width, bottom ); - LLTextBox::Params params; - params.name("MultiSliderCtrl Label"); - params.rect(label_rect); - params.initial_value(p.label()); - params.font(p.font); - mLabelBox = LLUICtrlFactory::create (params); - addChild(mLabelBox); - } - - S32 slider_right = getRect().getWidth(); - - if (p.show_text) - { - if (!p.text_width.isProvided()) - { - text_width = 0; - // calculate the size of the text box (log max_value is number of digits - 1 so plus 1) - if ( p.max_value() ) - text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 ); - - if ( p.increment < 1.0f ) - text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value - - if ( p.min_value < 0.0f || p.max_value < 0.0f ) - text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign - - // padding to make things look nicer - text_width += 8; - } - S32 text_left = getRect().getWidth() - text_width; - - slider_right = text_left - multi_sliderctrl_spacing; - - LLRect text_rect( text_left, top, getRect().getWidth(), bottom ); - if( p.can_edit_text ) - { - LLLineEditor::Params params; - params.name("MultiSliderCtrl Editor"); - params.rect(text_rect); - params.font(p.font); - params.max_length.bytes(MAX_STRING_LENGTH); - params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit); - params.prevalidator(&LLTextValidate::validateFloat); - params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); - mEditor = LLUICtrlFactory::create (params); - mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) ); - // don't do this, as selecting the entire text is single clicking in some cases - // and double clicking in others - //mEditor->setSelectAllonFocusReceived(true); - addChild(mEditor); - } - else - { - LLTextBox::Params params; - params.name("MultiSliderCtrl Text"); - params.rect(text_rect); - params.font(p.font); - params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); - mTextBox = LLUICtrlFactory::create (params); - addChild(mTextBox); - } - } - - S32 slider_left = label_width ? label_width + multi_sliderctrl_spacing : 0; - LLRect slider_rect( slider_left, top, slider_right, bottom ); - LLMultiSlider::Params params; - params.sliders = p.sliders; - params.rect(slider_rect); - params.commit_callback.function( LLMultiSliderCtrl::onSliderCommit ); - params.mouse_down_callback( p.mouse_down_callback ); - params.mouse_up_callback( p.mouse_up_callback ); - params.initial_value(p.initial_value()); - params.min_value(p.min_value); - params.max_value(p.max_value); - params.increment(p.increment); - params.max_sliders(p.max_sliders); - params.allow_overlap(p.allow_overlap); - params.loop_overlap(p.loop_overlap); - if (p.overlap_threshold.isProvided()) - { - params.overlap_threshold = p.overlap_threshold; - } - params.orientation(p.orientation); - params.thumb_image(p.thumb_image); - params.thumb_highlight_color(p.thumb_highlight_color); - if (p.thumb_width.isProvided()) - { - // otherwise should be provided by template - params.thumb_width(p.thumb_width); - } - params.draw_track(p.draw_track); - params.use_triangle(p.use_triangle); - params.control_name(p.control_name); - mMultiSlider = LLUICtrlFactory::create (params); - addChild( mMultiSlider ); - mCurValue = mMultiSlider->getCurSliderValue(); - - - updateText(); -} - -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); - mCurValue = mMultiSlider->getCurSliderValue(); - updateText(); -} - -void LLMultiSliderCtrl::setSliderValue(const std::string& name, F32 v, bool from_event) -{ - mMultiSlider->setSliderValue(name, v, from_event ); - mCurValue = mMultiSlider->getCurSliderValue(); - updateText(); -} - -void LLMultiSliderCtrl::setCurSlider(const std::string& name) -{ - mMultiSlider->setCurSlider(name); - mCurValue = mMultiSlider->getCurSliderValue(); -} - -void LLMultiSliderCtrl::resetCurSlider() -{ - mMultiSlider->resetCurSlider(); -} - -bool LLMultiSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - bool res = false; - if (mLabelBox) - { - res = mLabelBox->setTextArg(key, text); - if (res && mLabelWidth == 0) - { - S32 label_width = mFont->getWidth(mLabelBox->getText()); - LLRect rect = mLabelBox->getRect(); - S32 prev_right = rect.mRight; - rect.mRight = rect.mLeft + label_width; - mLabelBox->setRect(rect); - - S32 delta = rect.mRight - prev_right; - rect = mMultiSlider->getRect(); - S32 left = rect.mLeft + delta; - static LLUICachedControl multi_slider_ctrl_spacing ("UIMultiSliderctrlSpacing", 0); - left = llclamp(left, 0, rect.mRight - multi_slider_ctrl_spacing); - rect.mLeft = left; - mMultiSlider->setRect(rect); - } - } - return res; -} - -const std::string& LLMultiSliderCtrl::addSlider() -{ - const std::string& name = mMultiSlider->addSlider(); - - // if it returns null, pass it on - if(name == LLStringUtil::null) { - return LLStringUtil::null; - } - - // otherwise, update stuff - mCurValue = mMultiSlider->getCurSliderValue(); - updateText(); - return name; -} - -const std::string& LLMultiSliderCtrl::addSlider(F32 val) -{ - const std::string& name = mMultiSlider->addSlider(val); - - // if it returns null, pass it on - if(name == LLStringUtil::null) { - return LLStringUtil::null; - } - - // otherwise, update stuff - mCurValue = mMultiSlider->getCurSliderValue(); - updateText(); - return name; -} - -bool LLMultiSliderCtrl::addSlider(F32 val, const std::string& name) -{ - bool res = mMultiSlider->addSlider(val, name); - if (res) - { - mCurValue = mMultiSlider->getCurSliderValue(); - updateText(); - } - return res; -} - -void LLMultiSliderCtrl::deleteSlider(const std::string& name) -{ - mMultiSlider->deleteSlider(name); - mCurValue = mMultiSlider->getCurSliderValue(); - updateText(); -} - - -void LLMultiSliderCtrl::clear() -{ - setCurSliderValue(0.0f); - if( mEditor ) - { - mEditor->setText(std::string("")); - } - if( mTextBox ) - { - mTextBox->setText(std::string("")); - } - - // get rid of sliders - mMultiSlider->clear(); - -} - -bool LLMultiSliderCtrl::isMouseHeldDown() -{ - return gFocusMgr.getMouseCapture() == mMultiSlider; -} - -void LLMultiSliderCtrl::updateText() -{ - if( mEditor || mTextBox ) - { - LLLocale locale(LLLocale::USER_LOCALE); - - // Don't display very small negative values as -0.000 - F32 displayed_value = (F32)(floor(getCurSliderValue() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision)); - - std::string format = llformat("%%.%df", mPrecision); - std::string text = llformat(format.c_str(), displayed_value); - if( mEditor ) - { - mEditor->setText( text ); - } - else - { - mTextBox->setText( text ); - } - } -} - -// static -void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata) -{ - llassert(ctrl); - if (!ctrl) - return; - - LLMultiSliderCtrl* self = dynamic_cast(ctrl->getParent()); - llassert(self); - if (!self) // cast failed - wrong type! :O - return; - - bool success = false; - F32 val = self->mCurValue; - F32 saved_val = self->mCurValue; - - std::string text = self->mEditor->getText(); - if( LLLineEditor::postvalidateFloat( text ) ) - { - LLLocale locale(LLLocale::USER_LOCALE); - val = (F32) atof( text.c_str() ); - if( self->mMultiSlider->getMinValue() <= val && val <= self->mMultiSlider->getMaxValue() ) - { - self->setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it. - if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) ) - { - success = true; - } - } - } - - if( success ) - { - self->onCommit(); - } - else - { - if( self->getCurSliderValue() != saved_val ) - { - self->setCurSliderValue( saved_val ); - } - self->reportInvalidData(); - } - self->updateText(); -} - -// static -void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata) -{ - LLMultiSliderCtrl* self = dynamic_cast(ctrl->getParent()); - if (!self) - return; - - bool success = false; - F32 saved_val = self->mCurValue; - F32 new_val = self->mMultiSlider->getCurSliderValue(); - - self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it. - if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) ) - { - success = true; - } - - if( success ) - { - self->onCommit(); - } - else - { - if( self->mCurValue != saved_val ) - { - self->setCurSliderValue( saved_val ); - } - self->reportInvalidData(); - } - self->updateText(); -} - -void LLMultiSliderCtrl::setEnabled(bool b) -{ - LLF32UICtrl::setEnabled( b ); - - if( mLabelBox ) - { - mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); - } - - mMultiSlider->setEnabled( b ); - - if( mEditor ) - { - mEditor->setEnabled( b ); - } - - if( mTextBox ) - { - mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); - } -} - - -void LLMultiSliderCtrl::setTentative(bool b) -{ - if( mEditor ) - { - mEditor->setTentative(b); - } - LLF32UICtrl::setTentative(b); -} - - -void LLMultiSliderCtrl::onCommit() -{ - setTentative(false); - - if( mEditor ) - { - mEditor->setTentative(false); - } - - setControlValue(getValueF32()); - LLF32UICtrl::onCommit(); -} - - -void LLMultiSliderCtrl::setPrecision(S32 precision) -{ - if (precision < 0 || precision > 10) - { - LL_ERRS() << "LLMultiSliderCtrl::setPrecision - precision out of range" << LL_ENDL; - return; - } - - mPrecision = precision; - updateText(); -} - -boost::signals2::connection LLMultiSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ) -{ - return mMultiSlider->setMouseDownCallback( cb ); -} - -boost::signals2::connection LLMultiSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ) -{ - return mMultiSlider->setMouseUpCallback( cb ); -} - -void LLMultiSliderCtrl::onTabInto() -{ - if( mEditor ) - { - mEditor->onTabInto(); - } - LLF32UICtrl::onTabInto(); -} - -void LLMultiSliderCtrl::reportInvalidData() -{ - make_ui_sound("UISndBadKeystroke"); -} - -// virtual -void LLMultiSliderCtrl::setControlName(const std::string& control_name, LLView* context) -{ - mMultiSlider->setControlName(control_name, context); -} - +/** + * @file llmultisliderctrl.cpp + * @brief LLMultiSliderCtrl base class + * + * $LicenseInfo:firstyear=2007&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 "llmultisliderctrl.h" + +#include "llmath.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llkeyboard.h" +#include "lllineeditor.h" +#include "llmultislider.h" +#include "llstring.h" +#include "lltextbox.h" +#include "llui.h" +#include "lluiconstants.h" +#include "llcontrol.h" +#include "llfocusmgr.h" +#include "llresmgr.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register r("multi_slider"); + +const U32 MAX_STRING_LENGTH = 10; +LLMultiSliderCtrl::Params::Params() +: text_width("text_width"), + label_width("label_width"), + show_text("show_text", true), + can_edit_text("can_edit_text", false), + max_sliders("max_sliders", 1), + allow_overlap("allow_overlap", false), + loop_overlap("loop_overlap", false), + orientation("orientation"), + thumb_image("thumb_image"), + thumb_width("thumb_width"), + thumb_highlight_color("thumb_highlight_color"), + overlap_threshold("overlap_threshold", 0), + draw_track("draw_track", true), + use_triangle("use_triangle", false), + decimal_digits("decimal_digits", 3), + text_color("text_color"), + text_disabled_color("text_disabled_color"), + mouse_down_callback("mouse_down_callback"), + mouse_up_callback("mouse_up_callback"), + sliders("slider") +{ + mouse_opaque = true; +} + +LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p) +: LLF32UICtrl(p), + mLabelBox( NULL ), + mEditor( NULL ), + mTextBox( NULL ), + mTextEnabledColor(p.text_color()), + mTextDisabledColor(p.text_disabled_color()) +{ + static LLUICachedControl multi_sliderctrl_spacing ("UIMultiSliderctrlSpacing", 0); + + S32 top = getRect().getHeight(); + S32 bottom = 0; + S32 left = 0; + + S32 label_width = p.label_width; + S32 text_width = p.text_width; + + // Label + if( !p.label().empty() ) + { + if (p.label_width == 0) + { + label_width = p.font()->getWidth(p.label); + } + LLRect label_rect( left, top, label_width, bottom ); + LLTextBox::Params params; + params.name("MultiSliderCtrl Label"); + params.rect(label_rect); + params.initial_value(p.label()); + params.font(p.font); + mLabelBox = LLUICtrlFactory::create (params); + addChild(mLabelBox); + } + + S32 slider_right = getRect().getWidth(); + + if (p.show_text) + { + if (!p.text_width.isProvided()) + { + text_width = 0; + // calculate the size of the text box (log max_value is number of digits - 1 so plus 1) + if ( p.max_value() ) + text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 ); + + if ( p.increment < 1.0f ) + text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value + + if ( p.min_value < 0.0f || p.max_value < 0.0f ) + text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign + + // padding to make things look nicer + text_width += 8; + } + S32 text_left = getRect().getWidth() - text_width; + + slider_right = text_left - multi_sliderctrl_spacing; + + LLRect text_rect( text_left, top, getRect().getWidth(), bottom ); + if( p.can_edit_text ) + { + LLLineEditor::Params params; + params.name("MultiSliderCtrl Editor"); + params.rect(text_rect); + params.font(p.font); + params.max_length.bytes(MAX_STRING_LENGTH); + params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit); + params.prevalidator(&LLTextValidate::validateFloat); + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + mEditor = LLUICtrlFactory::create (params); + mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) ); + // don't do this, as selecting the entire text is single clicking in some cases + // and double clicking in others + //mEditor->setSelectAllonFocusReceived(true); + addChild(mEditor); + } + else + { + LLTextBox::Params params; + params.name("MultiSliderCtrl Text"); + params.rect(text_rect); + params.font(p.font); + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + mTextBox = LLUICtrlFactory::create (params); + addChild(mTextBox); + } + } + + S32 slider_left = label_width ? label_width + multi_sliderctrl_spacing : 0; + LLRect slider_rect( slider_left, top, slider_right, bottom ); + LLMultiSlider::Params params; + params.sliders = p.sliders; + params.rect(slider_rect); + params.commit_callback.function( LLMultiSliderCtrl::onSliderCommit ); + params.mouse_down_callback( p.mouse_down_callback ); + params.mouse_up_callback( p.mouse_up_callback ); + params.initial_value(p.initial_value()); + params.min_value(p.min_value); + params.max_value(p.max_value); + params.increment(p.increment); + params.max_sliders(p.max_sliders); + params.allow_overlap(p.allow_overlap); + params.loop_overlap(p.loop_overlap); + if (p.overlap_threshold.isProvided()) + { + params.overlap_threshold = p.overlap_threshold; + } + params.orientation(p.orientation); + params.thumb_image(p.thumb_image); + params.thumb_highlight_color(p.thumb_highlight_color); + if (p.thumb_width.isProvided()) + { + // otherwise should be provided by template + params.thumb_width(p.thumb_width); + } + params.draw_track(p.draw_track); + params.use_triangle(p.use_triangle); + params.control_name(p.control_name); + mMultiSlider = LLUICtrlFactory::create (params); + addChild( mMultiSlider ); + mCurValue = mMultiSlider->getCurSliderValue(); + + + updateText(); +} + +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); + mCurValue = mMultiSlider->getCurSliderValue(); + updateText(); +} + +void LLMultiSliderCtrl::setSliderValue(const std::string& name, F32 v, bool from_event) +{ + mMultiSlider->setSliderValue(name, v, from_event ); + mCurValue = mMultiSlider->getCurSliderValue(); + updateText(); +} + +void LLMultiSliderCtrl::setCurSlider(const std::string& name) +{ + mMultiSlider->setCurSlider(name); + mCurValue = mMultiSlider->getCurSliderValue(); +} + +void LLMultiSliderCtrl::resetCurSlider() +{ + mMultiSlider->resetCurSlider(); +} + +bool LLMultiSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + bool res = false; + if (mLabelBox) + { + res = mLabelBox->setTextArg(key, text); + if (res && mLabelWidth == 0) + { + S32 label_width = mFont->getWidth(mLabelBox->getText()); + LLRect rect = mLabelBox->getRect(); + S32 prev_right = rect.mRight; + rect.mRight = rect.mLeft + label_width; + mLabelBox->setRect(rect); + + S32 delta = rect.mRight - prev_right; + rect = mMultiSlider->getRect(); + S32 left = rect.mLeft + delta; + static LLUICachedControl multi_slider_ctrl_spacing ("UIMultiSliderctrlSpacing", 0); + left = llclamp(left, 0, rect.mRight - multi_slider_ctrl_spacing); + rect.mLeft = left; + mMultiSlider->setRect(rect); + } + } + return res; +} + +const std::string& LLMultiSliderCtrl::addSlider() +{ + const std::string& name = mMultiSlider->addSlider(); + + // if it returns null, pass it on + if(name == LLStringUtil::null) { + return LLStringUtil::null; + } + + // otherwise, update stuff + mCurValue = mMultiSlider->getCurSliderValue(); + updateText(); + return name; +} + +const std::string& LLMultiSliderCtrl::addSlider(F32 val) +{ + const std::string& name = mMultiSlider->addSlider(val); + + // if it returns null, pass it on + if(name == LLStringUtil::null) { + return LLStringUtil::null; + } + + // otherwise, update stuff + mCurValue = mMultiSlider->getCurSliderValue(); + updateText(); + return name; +} + +bool LLMultiSliderCtrl::addSlider(F32 val, const std::string& name) +{ + bool res = mMultiSlider->addSlider(val, name); + if (res) + { + mCurValue = mMultiSlider->getCurSliderValue(); + updateText(); + } + return res; +} + +void LLMultiSliderCtrl::deleteSlider(const std::string& name) +{ + mMultiSlider->deleteSlider(name); + mCurValue = mMultiSlider->getCurSliderValue(); + updateText(); +} + + +void LLMultiSliderCtrl::clear() +{ + setCurSliderValue(0.0f); + if( mEditor ) + { + mEditor->setText(std::string("")); + } + if( mTextBox ) + { + mTextBox->setText(std::string("")); + } + + // get rid of sliders + mMultiSlider->clear(); + +} + +bool LLMultiSliderCtrl::isMouseHeldDown() +{ + return gFocusMgr.getMouseCapture() == mMultiSlider; +} + +void LLMultiSliderCtrl::updateText() +{ + if( mEditor || mTextBox ) + { + LLLocale locale(LLLocale::USER_LOCALE); + + // Don't display very small negative values as -0.000 + F32 displayed_value = (F32)(floor(getCurSliderValue() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision)); + + std::string format = llformat("%%.%df", mPrecision); + std::string text = llformat(format.c_str(), displayed_value); + if( mEditor ) + { + mEditor->setText( text ); + } + else + { + mTextBox->setText( text ); + } + } +} + +// static +void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata) +{ + llassert(ctrl); + if (!ctrl) + return; + + LLMultiSliderCtrl* self = dynamic_cast(ctrl->getParent()); + llassert(self); + if (!self) // cast failed - wrong type! :O + return; + + bool success = false; + F32 val = self->mCurValue; + F32 saved_val = self->mCurValue; + + std::string text = self->mEditor->getText(); + if( LLLineEditor::postvalidateFloat( text ) ) + { + LLLocale locale(LLLocale::USER_LOCALE); + val = (F32) atof( text.c_str() ); + if( self->mMultiSlider->getMinValue() <= val && val <= self->mMultiSlider->getMaxValue() ) + { + self->setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it. + if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) ) + { + success = true; + } + } + } + + if( success ) + { + self->onCommit(); + } + else + { + if( self->getCurSliderValue() != saved_val ) + { + self->setCurSliderValue( saved_val ); + } + self->reportInvalidData(); + } + self->updateText(); +} + +// static +void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata) +{ + LLMultiSliderCtrl* self = dynamic_cast(ctrl->getParent()); + if (!self) + return; + + bool success = false; + F32 saved_val = self->mCurValue; + F32 new_val = self->mMultiSlider->getCurSliderValue(); + + self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it. + if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) ) + { + success = true; + } + + if( success ) + { + self->onCommit(); + } + else + { + if( self->mCurValue != saved_val ) + { + self->setCurSliderValue( saved_val ); + } + self->reportInvalidData(); + } + self->updateText(); +} + +void LLMultiSliderCtrl::setEnabled(bool b) +{ + LLF32UICtrl::setEnabled( b ); + + if( mLabelBox ) + { + mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); + } + + mMultiSlider->setEnabled( b ); + + if( mEditor ) + { + mEditor->setEnabled( b ); + } + + if( mTextBox ) + { + mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); + } +} + + +void LLMultiSliderCtrl::setTentative(bool b) +{ + if( mEditor ) + { + mEditor->setTentative(b); + } + LLF32UICtrl::setTentative(b); +} + + +void LLMultiSliderCtrl::onCommit() +{ + setTentative(false); + + if( mEditor ) + { + mEditor->setTentative(false); + } + + setControlValue(getValueF32()); + LLF32UICtrl::onCommit(); +} + + +void LLMultiSliderCtrl::setPrecision(S32 precision) +{ + if (precision < 0 || precision > 10) + { + LL_ERRS() << "LLMultiSliderCtrl::setPrecision - precision out of range" << LL_ENDL; + return; + } + + mPrecision = precision; + updateText(); +} + +boost::signals2::connection LLMultiSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ) +{ + return mMultiSlider->setMouseDownCallback( cb ); +} + +boost::signals2::connection LLMultiSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ) +{ + return mMultiSlider->setMouseUpCallback( cb ); +} + +void LLMultiSliderCtrl::onTabInto() +{ + if( mEditor ) + { + mEditor->onTabInto(); + } + LLF32UICtrl::onTabInto(); +} + +void LLMultiSliderCtrl::reportInvalidData() +{ + make_ui_sound("UISndBadKeystroke"); +} + +// virtual +void LLMultiSliderCtrl::setControlName(const std::string& control_name, LLView* context) +{ + mMultiSlider->setControlName(control_name, context); +} + diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h index a369140660..fee05c92fd 100644 --- a/indra/llui/llmultisliderctrl.h +++ b/indra/llui/llmultisliderctrl.h @@ -1,174 +1,174 @@ -/** - * @file llmultisliderctrl.h - * @brief LLMultiSliderCtrl base class - * - * $LicenseInfo:firstyear=2007&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_MULTI_SLIDERCTRL_H -#define LL_MULTI_SLIDERCTRL_H - -#include "llf32uictrl.h" -#include "v4color.h" -#include "llmultislider.h" -#include "lltextbox.h" -#include "llrect.h" - - -// -// Classes -// -class LLFontGL; -class LLLineEditor; -class LLSlider; - - -class LLMultiSliderCtrl : public LLF32UICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Optional label_width, - text_width; - Optional show_text, - can_edit_text; - Optional decimal_digits, - thumb_width; - Optional max_sliders; - Optional allow_overlap, - loop_overlap, - draw_track, - use_triangle; - - Optional orientation, - thumb_image; - - Optional overlap_threshold; - - Optional text_color, - text_disabled_color, - thumb_highlight_color; - - Optional mouse_down_callback, - mouse_up_callback; - - Multiple sliders; - - Params(); - }; - -protected: - LLMultiSliderCtrl(const Params&); - friend class LLUICtrlFactory; -public: - virtual ~LLMultiSliderCtrl(); - - F32 getSliderValue(const std::string& name) const { return mMultiSlider->getSliderValue(name); } - void setSliderValue(const std::string& name, F32 v, bool from_event = false); - - virtual void setValue(const LLSD& value ); - virtual LLSD getValue() const { return mMultiSlider->getValue(); } - virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - - const std::string& getCurSlider() const { return mMultiSlider->getCurSlider(); } - F32 getCurSliderValue() const { return mCurValue; } - void setCurSlider(const std::string& name); - void resetCurSlider(); - void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); } - - virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } - virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } - - bool isMouseHeldDown(); - - virtual void setEnabled( bool b ); - virtual void clear(); - virtual void setPrecision(S32 precision); - void setMinValue(F32 min_value) {mMultiSlider->setMinValue(min_value);} - void setMaxValue(F32 max_value) {mMultiSlider->setMaxValue(max_value);} - void setIncrement(F32 increment) {mMultiSlider->setIncrement(increment);} - - F32 getNearestIncrement(F32 value) const { return mMultiSlider->getNearestIncrement(value); } - F32 getSliderValueFromPos(S32 x, S32 y) const { return mMultiSlider->getSliderValueFromPos(x, y); } - LLRect getSliderThumbRect(const std::string &name) const { return mMultiSlider->getSliderThumbRect(name); } - - void setSliderThumbImage(const std::string &name) { mMultiSlider->setSliderThumbImage(name); } - void clearSliderThumbImage() { mMultiSlider->clearSliderThumbImage(); } - - /// for adding and deleting sliders - const std::string& addSlider(); - const std::string& addSlider(F32 val); - bool addSlider(F32 val, const std::string& name); - void deleteSlider(const std::string& name); - void deleteCurSlider() { deleteSlider(mMultiSlider->getCurSlider()); } - - F32 getMinValue() const { return mMultiSlider->getMinValue(); } - F32 getMaxValue() const { return mMultiSlider->getMaxValue(); } - - S32 getMaxNumSliders() { return mMultiSlider->getMaxNumSliders(); } - S32 getCurNumSliders() { return mMultiSlider->getCurNumSliders(); } - F32 getOverlapThreshold() { return mMultiSlider->getOverlapThreshold(); } - bool canAddSliders() { return mMultiSlider->canAddSliders(); } - - void setLabel(const std::string& label) { if (mLabelBox) mLabelBox->setText(label); } - void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; } - void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; } - - boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ); - - virtual void onTabInto(); - - virtual void setTentative(bool b); // marks value as tentative - virtual void onCommit(); // mark not tentative, then commit - - virtual void setControlName(const std::string& control_name, LLView* context); - - static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata); - - static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata); - static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); - static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); - -private: - void updateText(); - void reportInvalidData(); - -private: - const LLFontGL* mFont; - bool mShowText; - bool mCanEditText; - - S32 mPrecision; - LLTextBox* mLabelBox; - S32 mLabelWidth; - - F32 mCurValue; - LLMultiSlider* mMultiSlider; - LLLineEditor* mEditor; - LLTextBox* mTextBox; - - LLUIColor mTextEnabledColor; - LLUIColor mTextDisabledColor; -}; - -#endif // LL_MULTI_SLIDERCTRL_H +/** + * @file llmultisliderctrl.h + * @brief LLMultiSliderCtrl base class + * + * $LicenseInfo:firstyear=2007&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_MULTI_SLIDERCTRL_H +#define LL_MULTI_SLIDERCTRL_H + +#include "llf32uictrl.h" +#include "v4color.h" +#include "llmultislider.h" +#include "lltextbox.h" +#include "llrect.h" + + +// +// Classes +// +class LLFontGL; +class LLLineEditor; +class LLSlider; + + +class LLMultiSliderCtrl : public LLF32UICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional label_width, + text_width; + Optional show_text, + can_edit_text; + Optional decimal_digits, + thumb_width; + Optional max_sliders; + Optional allow_overlap, + loop_overlap, + draw_track, + use_triangle; + + Optional orientation, + thumb_image; + + Optional overlap_threshold; + + Optional text_color, + text_disabled_color, + thumb_highlight_color; + + Optional mouse_down_callback, + mouse_up_callback; + + Multiple sliders; + + Params(); + }; + +protected: + LLMultiSliderCtrl(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLMultiSliderCtrl(); + + F32 getSliderValue(const std::string& name) const { return mMultiSlider->getSliderValue(name); } + void setSliderValue(const std::string& name, F32 v, bool from_event = false); + + virtual void setValue(const LLSD& value ); + virtual LLSD getValue() const { return mMultiSlider->getValue(); } + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); + + const std::string& getCurSlider() const { return mMultiSlider->getCurSlider(); } + F32 getCurSliderValue() const { return mCurValue; } + void setCurSlider(const std::string& name); + void resetCurSlider(); + void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); } + + virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } + virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } + + bool isMouseHeldDown(); + + virtual void setEnabled( bool b ); + virtual void clear(); + virtual void setPrecision(S32 precision); + void setMinValue(F32 min_value) {mMultiSlider->setMinValue(min_value);} + void setMaxValue(F32 max_value) {mMultiSlider->setMaxValue(max_value);} + void setIncrement(F32 increment) {mMultiSlider->setIncrement(increment);} + + F32 getNearestIncrement(F32 value) const { return mMultiSlider->getNearestIncrement(value); } + F32 getSliderValueFromPos(S32 x, S32 y) const { return mMultiSlider->getSliderValueFromPos(x, y); } + LLRect getSliderThumbRect(const std::string &name) const { return mMultiSlider->getSliderThumbRect(name); } + + void setSliderThumbImage(const std::string &name) { mMultiSlider->setSliderThumbImage(name); } + void clearSliderThumbImage() { mMultiSlider->clearSliderThumbImage(); } + + /// for adding and deleting sliders + const std::string& addSlider(); + const std::string& addSlider(F32 val); + bool addSlider(F32 val, const std::string& name); + void deleteSlider(const std::string& name); + void deleteCurSlider() { deleteSlider(mMultiSlider->getCurSlider()); } + + F32 getMinValue() const { return mMultiSlider->getMinValue(); } + F32 getMaxValue() const { return mMultiSlider->getMaxValue(); } + + S32 getMaxNumSliders() { return mMultiSlider->getMaxNumSliders(); } + S32 getCurNumSliders() { return mMultiSlider->getCurNumSliders(); } + F32 getOverlapThreshold() { return mMultiSlider->getOverlapThreshold(); } + bool canAddSliders() { return mMultiSlider->canAddSliders(); } + + void setLabel(const std::string& label) { if (mLabelBox) mLabelBox->setText(label); } + void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; } + void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; } + + boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ); + + virtual void onTabInto(); + + virtual void setTentative(bool b); // marks value as tentative + virtual void onCommit(); // mark not tentative, then commit + + virtual void setControlName(const std::string& control_name, LLView* context); + + static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata); + + static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata); + static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); + static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); + +private: + void updateText(); + void reportInvalidData(); + +private: + const LLFontGL* mFont; + bool mShowText; + bool mCanEditText; + + S32 mPrecision; + LLTextBox* mLabelBox; + S32 mLabelWidth; + + F32 mCurValue; + LLMultiSlider* mMultiSlider; + LLLineEditor* mEditor; + LLTextBox* mTextBox; + + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; +}; + +#endif // LL_MULTI_SLIDERCTRL_H diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 9549860972..74e573bb4e 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1,2009 +1,2009 @@ -/** -* @file llnotifications.cpp -* @brief Non-UI queue manager for keeping a prioritized list of notifications -* -* $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 "llnotifications.h" -#include "llnotificationtemplate.h" -#include "llnotificationvisibilityrule.h" - -#include "llavatarnamecache.h" -#include "llinstantmessage.h" -#include "llcachename.h" -#include "llxmlnode.h" -#include "lluictrl.h" -#include "lluictrlfactory.h" -#include "lldir.h" -#include "llsdserialize.h" -#include "lltrans.h" -#include "llstring.h" -#include "llsdparam.h" -#include "llsdutil.h" - -#include -#include - - -const std::string NOTIFICATION_PERSIST_VERSION = "0.93"; - -void NotificationPriorityValues::declareValues() -{ - declare("low", NOTIFICATION_PRIORITY_LOW); - declare("normal", NOTIFICATION_PRIORITY_NORMAL); - declare("high", NOTIFICATION_PRIORITY_HIGH); - declare("critical", NOTIFICATION_PRIORITY_CRITICAL); -} - -LLNotificationForm::FormElementBase::FormElementBase() -: name("name"), - enabled("enabled", true) -{} - -LLNotificationForm::FormIgnore::FormIgnore() -: text("text"), - control("control"), - invert_control("invert_control", false), - save_option("save_option", false), - session_only("session_only", false), - checkbox_only("checkbox_only", false) -{} - -LLNotificationForm::FormButton::FormButton() -: index("index"), - text("text"), - ignore("ignore"), - is_default("default"), - width("width", 0), - type("type") -{ - // set type here so it gets serialized - type = "button"; -} - -LLNotificationForm::FormInput::FormInput() -: type("type"), - text("text"), - max_length_chars("max_length_chars"), - allow_emoji("allow_emoji"), - width("width", 0), - value("value") -{} - -LLNotificationForm::FormElement::FormElement() -: button("button"), - input("input") -{} - -LLNotificationForm::FormElements::FormElements() -: elements("") -{} - -LLNotificationForm::Params::Params() -: name("name"), - ignore("ignore"), - form_elements("") -{} - - - -bool filterIgnoredNotifications(LLNotificationPtr notification) -{ - LLNotificationFormPtr form = notification->getForm(); - // Check to see if the user wants to ignore this alert - return !notification->getForm()->getIgnored(); -} - -bool handleIgnoredNotification(const LLSD& payload) -{ - if (payload["sigtype"].asString() == "add") - { - LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); - if (!pNotif) return false; - - LLNotificationFormPtr form = pNotif->getForm(); - LLSD response; - switch(form->getIgnoreType()) - { - case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE: - case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY: - response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON); - break; - case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE: - response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName()); - break; - case LLNotificationForm::IGNORE_SHOW_AGAIN: - break; - default: - return false; - } - pNotif->setIgnored(true); - pNotif->respond(response); - return true; // don't process this item any further - } - return false; -} - -bool defaultResponse(const LLSD& payload) -{ - if (payload["sigtype"].asString() == "add") - { - LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); - if (pNotif) - { - // supply default response - pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON)); - } - } - return false; -} - -bool visibilityRuleMached(const LLSD& payload) -{ - // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification. - // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order. - return true; -} - - -namespace LLNotificationFilters -{ - // a sample filter - bool includeEverything(LLNotificationPtr p) - { - return true; - } -}; - -LLNotificationForm::LLNotificationForm() -: mIgnore(IGNORE_NO) -{ -} - -LLNotificationForm::LLNotificationForm( const LLNotificationForm& other ) -{ - mFormData = other.mFormData; - mIgnore = other.mIgnore; - mIgnoreMsg = other.mIgnoreMsg; - mIgnoreSetting = other.mIgnoreSetting; - mInvertSetting = other.mInvertSetting; -} - -LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p) -: mIgnore(IGNORE_NO), - mInvertSetting(false) // ignore settings by default mean true=show, false=ignore -{ - if (p.ignore.isProvided()) - { - // For all cases but IGNORE_CHECKBOX_ONLY this is name for use in preferences - mIgnoreMsg = p.ignore.text; - - LLUI *ui_inst = LLUI::getInstance(); - if (p.ignore.checkbox_only) - { - mIgnore = IGNORE_CHECKBOX_ONLY; - } - else if (!p.ignore.save_option) - { - mIgnore = p.ignore.session_only ? IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY : IGNORE_WITH_DEFAULT_RESPONSE; - } - else - { - // remember last option chosen by user and automatically respond with that in the future - mIgnore = IGNORE_WITH_LAST_RESPONSE; - ui_inst->mSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name)); - } - - bool show_notification = true; - if (p.ignore.control.isProvided()) - { - mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control()); - mInvertSetting = p.ignore.invert_control; - } - else if (mIgnore > IGNORE_NO) - { - ui_inst->mSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT); - mIgnoreSetting = ui_inst->mSettingGroups["ignores"]->getControl(name); - } - } - - LLParamSDParser parser; - parser.writeSD(mFormData, p.form_elements); - - for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray(); - it != end_it; - ++it) - { - // lift contents of form element up a level, since element type is already encoded in "type" param - if (it->isMap() && it->beginMap() != it->endMap()) - { - *it = it->beginMap()->second; - } - } - - LL_DEBUGS("Notifications") << name << LL_ENDL; - LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL; -} - -LLNotificationForm::LLNotificationForm(const LLSD& sd) - : mIgnore(IGNORE_NO) -{ - if (sd.isArray()) - { - mFormData = sd; - } - else - { - LL_WARNS("Notifications") << "Invalid form data " << sd << LL_ENDL; - mFormData = LLSD::emptyArray(); - } -} - -LLSD LLNotificationForm::asLLSD() const -{ - return mFormData; -} - -LLSD LLNotificationForm::getElement(const std::string& element_name) -{ - for (LLSD::array_const_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["name"].asString() == element_name) return (*it); - } - return LLSD(); -} - - -bool LLNotificationForm::hasElement(const std::string& element_name) const -{ - for (LLSD::array_const_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["name"].asString() == element_name) return true; - } - return false; -} - -void LLNotificationForm::getElements(LLSD& elements, S32 offset) -{ - //Finds elements that the template did not add - LLSD::array_const_iterator it = mFormData.beginArray() + offset; - - //Keeps track of only the dynamic elements - for(; it != mFormData.endArray(); ++it) - { - elements.append(*it); - } -} - -bool LLNotificationForm::getElementEnabled(const std::string& element_name) const -{ - for (LLSD::array_const_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["name"].asString() == element_name) - { - return (*it)["enabled"].asBoolean(); - } - } - - return false; -} - -void LLNotificationForm::setElementEnabled(const std::string& element_name, bool enabled) -{ - for (LLSD::array_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["name"].asString() == element_name) - { - (*it)["enabled"] = enabled; - } - } -} - - -void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value, bool enabled) -{ - LLSD element; - element["type"] = type; - element["name"] = name; - element["text"] = name; - element["value"] = value; - element["index"] = LLSD::Integer(mFormData.size()); - element["enabled"] = enabled; - mFormData.append(element); -} - -void LLNotificationForm::append(const LLSD& sub_form) -{ - if (sub_form.isArray()) - { - for (LLSD::array_const_iterator it = sub_form.beginArray(); - it != sub_form.endArray(); - ++it) - { - mFormData.append(*it); - } - } -} - -void LLNotificationForm::formatElements(const LLSD& substitutions) -{ - for (LLSD::array_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - // format "text" component of each form element - if ((*it).has("text")) - { - std::string text = (*it)["text"].asString(); - LLStringUtil::format(text, substitutions); - (*it)["text"] = text; - } - if ((*it)["type"].asString() == "text" && (*it).has("value")) - { - std::string value = (*it)["value"].asString(); - LLStringUtil::format(value, substitutions); - (*it)["value"] = value; - } - } -} - -std::string LLNotificationForm::getDefaultOption() -{ - for (LLSD::array_const_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["default"]) return (*it)["name"].asString(); - } - return ""; -} - -LLControlVariablePtr LLNotificationForm::getIgnoreSetting() -{ - return mIgnoreSetting; -} - -bool LLNotificationForm::getIgnored() -{ - bool show = true; - if (mIgnore > LLNotificationForm::IGNORE_NO - && mIgnoreSetting) - { - show = mIgnoreSetting->getValue().asBoolean(); - if (mInvertSetting) show = !show; - } - return !show; -} - -void LLNotificationForm::setIgnored(bool ignored) -{ - if (mIgnoreSetting) - { - if (mInvertSetting) ignored = !ignored; - mIgnoreSetting->setValue(!ignored); - } -} - -LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p) -: mName(p.name), - mType(p.type), - mMessage(p.value), - mFooter(p.footer.value), - mLabel(p.label), - mIcon(p.icon), - mURL(p.url.value), - mExpireSeconds(p.duration), - mExpireOption(p.expire_option), - mURLOption(p.url.option), - mURLTarget(p.url.target), - mForceUrlsExternal(p.force_urls_external), - mUnique(p.unique.isProvided()), - mCombineBehavior(p.unique.combine), - mPriority(p.priority), - mPersist(p.persist), - mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()), - mLogToChat(p.log_to_chat), - mLogToIM(p.log_to_im), - mShowToast(p.show_toast), - mFadeToast(p.fade_toast), - mSoundName("") -{ - if (p.sound.isProvided() - && LLUI::getInstance()->mSettingGroups["config"]->controlExists(p.sound)) - { - mSoundName = p.sound; - } - - for (const LLNotificationTemplate::UniquenessContext& context : p.unique.contexts) - { - mUniqueContext.push_back(context.value); - } - - LL_DEBUGS("Notifications") << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL; - - for (const LLNotificationTemplate::Tag& tag : p.tags) - { - LL_DEBUGS("Notifications") << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL; - mTags.push_back(tag.value); - } - - mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form)); -} - -LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p) -{ - if (p.show.isChosen()) - { - mType = p.show.type; - mTag = p.show.tag; - mName = p.show.name; - mVisible = true; - } - else if (p.hide.isChosen()) - { - mType = p.hide.type; - mTag = p.hide.tag; - mName = p.hide.name; - mVisible = false; - } - else if (p.respond.isChosen()) - { - mType = p.respond.type; - mTag = p.respond.tag; - mName = p.respond.name; - mVisible = false; - mResponse = p.respond.response; - } -} - -LLNotification::LLNotification(const LLSDParamAdapter& p) : - mTimestamp(p.time_stamp), - mSubstitutions(p.substitutions), - mPayload(p.payload), - mExpiresAt(p.expiry), - mTemporaryResponder(false), - mRespondedTo(false), - mPriority(p.priority), - mCancelled(false), - mIgnored(false), - mResponderObj(NULL), - mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()), - mOfferFromAgent(p.offer_from_agent), - mIsDND(p.is_dnd) -{ - if (p.functor.name.isChosen()) - { - mResponseFunctorName = p.functor.name; - } - else if (p.functor.function.isChosen()) - { - mResponseFunctorName = LLUUID::generateNewID().asString(); - LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function()); - - mTemporaryResponder = true; - } - else if(p.functor.responder.isChosen()) - { - mResponder = p.functor.responder; - } - - if(p.responder.isProvided()) - { - mResponderObj = p.responder; - } - - init(p.name, p.form_elements); -} - - -LLSD LLNotification::asLLSD(bool excludeTemplateElements) -{ - LLParamSDParser parser; - - Params p; - p.id = mId; - p.name = mTemplatep->mName; - p.substitutions = mSubstitutions; - p.payload = mPayload; - p.time_stamp = mTimestamp; - p.expiry = mExpiresAt; - p.priority = mPriority; - - LLNotificationFormPtr templateForm = mTemplatep->mForm; - LLSD formElements = mForm->asLLSD(); - - //All form elements (dynamic or not) - if(!excludeTemplateElements) - { - p.form_elements = formElements; - } - //Only dynamic form elements (exclude template elements) - else if(templateForm->getNumElements() < formElements.size()) - { - LLSD dynamicElements; - //Offset to dynamic elements and store them - mForm->getElements(dynamicElements, templateForm->getNumElements()); - p.form_elements = dynamicElements; - } - - if(mResponder) - { - p.functor.responder_sd = mResponder->asLLSD(); - } - - if(!mResponseFunctorName.empty()) - { - p.functor.name = mResponseFunctorName; - } - - LLSD output; - parser.writeSD(output, p); - return output; -} - -void LLNotification::update() -{ - LLNotifications::instance().update(shared_from_this()); -} - -void LLNotification::updateFrom(LLNotificationPtr other) -{ - // can only update from the same notification type - if (mTemplatep != other->mTemplatep) return; - - // NOTE: do NOT change the ID, since it is the key to - // this given instance, just update all the metadata - //mId = other->mId; - - mPayload = other->mPayload; - mSubstitutions = other->mSubstitutions; - mTimestamp = other->mTimestamp; - mExpiresAt = other->mExpiresAt; - mCancelled = other->mCancelled; - mIgnored = other->mIgnored; - mPriority = other->mPriority; - mForm = other->mForm; - mResponseFunctorName = other->mResponseFunctorName; - mRespondedTo = other->mRespondedTo; - mResponse = other->mResponse; - mTemporaryResponder = other->mTemporaryResponder; - - update(); -} - -const LLNotificationFormPtr LLNotification::getForm() -{ - return mForm; -} - -void LLNotification::cancel() -{ - mCancelled = true; -} - -LLSD LLNotification::getResponseTemplate(EResponseTemplateType type) -{ - LLSD response = LLSD::emptyMap(); - for (S32 element_idx = 0; - element_idx < mForm->getNumElements(); - ++element_idx) - { - LLSD element = mForm->getElement(element_idx); - if (element.has("name")) - { - response[element["name"].asString()] = element["value"]; - } - - if ((type == WITH_DEFAULT_BUTTON) - && element["default"].asBoolean()) - { - response[element["name"].asString()] = true; - } - } - return response; -} - -//static -S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response) -{ - LLNotificationForm form(notification["form"]); - - for (S32 element_idx = 0; - element_idx < form.getNumElements(); - ++element_idx) - { - LLSD element = form.getElement(element_idx); - - // only look at buttons - if (element["type"].asString() == "button" - && response[element["name"].asString()].asBoolean()) - { - return element["index"].asInteger(); - } - } - - return -1; -} - -//static -std::string LLNotification::getSelectedOptionName(const LLSD& response) -{ - for (LLSD::map_const_iterator response_it = response.beginMap(); - response_it != response.endMap(); - ++response_it) - { - if (response_it->second.isBoolean() && response_it->second.asBoolean()) - { - return response_it->first; - } - } - return ""; -} - - -void LLNotification::respond(const LLSD& response) -{ - // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo() - mRespondedTo = true; - mResponse = response; - - if(mResponder) - { - mResponder->handleRespond(asLLSD(), response); - } - else if (!mResponseFunctorName.empty()) - { - // look up the functor - LLNotificationFunctorRegistry::ResponseFunctor functor = - LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName); - // and then call it - functor(asLLSD(), response); - } - else if (mCombinedNotifications.empty()) - { - // no registered responder - return; - } - - if (mTemporaryResponder) - { - LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); - mResponseFunctorName = ""; - mTemporaryResponder = false; - } - - if (mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO) - { - mForm->setIgnored(mIgnored); - if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) - { - LLUI::getInstance()->mSettingGroups["ignores"]->setLLSD("Default" + getName(), response); - } - } - - for (std::vector::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it) - { - if ((*it)) - { - (*it)->respond(response); - } - } - - update(); -} - -void LLNotification::respondWithDefault() -{ - respond(getResponseTemplate(WITH_DEFAULT_BUTTON)); -} - - -const std::string& LLNotification::getName() const -{ - return mTemplatep->mName; -} - -const std::string& LLNotification::getIcon() const -{ - return mTemplatep->mIcon; -} - - -bool LLNotification::isPersistent() const -{ - return mTemplatep->mPersist; -} - -std::string LLNotification::getType() const -{ - return (mTemplatep ? mTemplatep->mType : ""); -} - -S32 LLNotification::getURLOption() const -{ - return (mTemplatep ? mTemplatep->mURLOption : -1); -} - -S32 LLNotification::getURLOpenExternally() const -{ - return(mTemplatep? mTemplatep->mURLTarget == "_external": -1); -} - -bool LLNotification::getForceUrlsExternal() const -{ - return (mTemplatep ? mTemplatep->mForceUrlsExternal : false); -} - -bool LLNotification::hasUniquenessConstraints() const -{ - return (mTemplatep ? mTemplatep->mUnique : false); -} - -bool LLNotification::matchesTag(const std::string& tag) -{ - bool result = false; - - if(mTemplatep) - { - std::list::iterator it; - for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++) - { - if((*it) == tag) - { - result = true; - break; - } - } - } - - return result; -} - -void LLNotification::setIgnored(bool ignore) -{ - mIgnored = ignore; -} - -void LLNotification::setResponseFunctor(std::string const &responseFunctorName) -{ - if (mTemporaryResponder) - // get rid of the old one - LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); - mResponseFunctorName = responseFunctorName; - mTemporaryResponder = false; -} - -void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb) -{ - if(mTemporaryResponder) - { - LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); - } - - LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb); -} - -void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder) -{ - mResponder = responder; -} - -bool LLNotification::isEquivalentTo(LLNotificationPtr that) const -{ - if (this->mTemplatep->mName != that->mTemplatep->mName) - { - return false; // must have the same template name or forget it - } - if (this->mTemplatep->mUnique) - { - const LLSD& these_substitutions = this->getSubstitutions(); - const LLSD& those_substitutions = that->getSubstitutions(); - const LLSD& this_payload = this->getPayload(); - const LLSD& that_payload = that->getPayload(); - - // highlander bit sez there can only be one of these - for (std::vector::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end(); - it != end_it; - ++it) - { - // if templates differ in either substitution strings or payload with the given field name - // then they are considered inequivalent - // use of get() avoids converting the LLSD value to a map as the [] operator would - if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString() - || this_payload.get(*it).asString() != that_payload.get(*it).asString()) - { - return false; - } - } - return true; - } - - return false; -} - -void LLNotification::init(const std::string& template_name, const LLSD& form_elements) -{ - mTemplatep = LLNotifications::instance().getTemplate(template_name); - if (!mTemplatep) return; - - // add default substitutions - const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs(); - for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin(); - iter != default_args.end(); ++iter) - { - mSubstitutions[iter->first] = iter->second; - } - mSubstitutions["_URL"] = getURL(); - mSubstitutions["_NAME"] = template_name; - // TODO: something like this so that a missing alert is sensible: - //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions); - - mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm)); - mForm->append(form_elements); - - // apply substitution to form labels - mForm->formatElements(mSubstitutions); - - mIgnored = mForm->getIgnored(); - - LLDate rightnow = LLDate::now(); - if (mTemplatep->mExpireSeconds) - { - mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds); - } - - if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED) - { - mPriority = mTemplatep->mPriority; - } -} - -std::string LLNotification::summarize() const -{ - std::string s = "Notification("; - s += getName(); - s += ") : "; - s += mTemplatep ? mTemplatep->mMessage : ""; - // should also include timestamp and expiration time (but probably not payload) - return s; -} - -std::string LLNotification::getMessage() const -{ - // all our callers cache this result, so it gives us more flexibility - // to do the substitution at call time rather than attempting to - // cache it in the notification - if (!mTemplatep) - return std::string(); - - std::string message = mTemplatep->mMessage; - LLStringUtil::format(message, mSubstitutions); - return message; -} - -std::string LLNotification::getFooter() const -{ - if (!mTemplatep) - return std::string(); - - std::string footer = mTemplatep->mFooter; - LLStringUtil::format(footer, mSubstitutions); - return footer; -} - -std::string LLNotification::getLabel() const -{ - std::string label = mTemplatep->mLabel; - LLStringUtil::format(label, mSubstitutions); - return (mTemplatep ? label : ""); -} - -std::string LLNotification::getURL() const -{ - if (!mTemplatep) - return std::string(); - std::string url = mTemplatep->mURL; - LLStringUtil::format(url, mSubstitutions); - return (mTemplatep ? url : ""); -} - -bool LLNotification::canLogToChat() const -{ - return mTemplatep->mLogToChat; -} - -bool LLNotification::canLogToIM() const -{ - return mTemplatep->mLogToIM; -} - -bool LLNotification::canShowToast() const -{ - return mTemplatep->mShowToast; -} - -bool LLNotification::canFadeToast() const -{ - return mTemplatep->mFadeToast; -} - -bool LLNotification::hasFormElements() const -{ - return mTemplatep->mForm->getNumElements() != 0; -} - -void LLNotification::playSound() -{ - make_ui_sound(mTemplatep->mSoundName.c_str()); -} - -LLNotification::ECombineBehavior LLNotification::getCombineBehavior() const -{ - return mTemplatep->mCombineBehavior; -} - -void LLNotification::updateForm( const LLNotificationFormPtr& form ) -{ - mForm = form; -} - -void LLNotification::repost() -{ - mRespondedTo = false; - LLNotifications::instance().update(shared_from_this()); -} - - - -// ========================================================= -// LLNotificationChannel implementation -// --- -LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot) -{ - // when someone wants to connect to a channel, we first throw them - // all of the notifications that are already in the channel - // we use a special signal called "load" in case the channel wants to care - // only about new notifications - LLMutexLock lock(&mItemsMutex); - for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) - { - slot(LLSD().with("sigtype", "load").with("id", (*it)->id())); - } - // and then connect the signal so that all future notifications will also be - // forwarded. - return mChanged.connect(slot); -} - -LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot) -{ - for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) - { - slot(LLSD().with("sigtype", "load").with("id", (*it)->id())); - } - return mChanged.connect(slot, boost::signals2::at_front); -} - -LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot) -{ - // these two filters only fire for notifications added after the current one, because - // they don't participate in the hierarchy. - return mPassedFilter.connect(slot); -} - -LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot) -{ - return mFailedFilter.connect(slot); -} - -// external call, conforms to our standard signature -bool LLNotificationChannelBase::updateItem(const LLSD& payload) -{ - // first check to see if it's in the master list - LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]); - if (!pNotification) - return false; // not found - - return updateItem(payload, pNotification); -} - - -//FIX QUIT NOT WORKING - - -// internal call, for use in avoiding lookup -bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification) -{ - std::string cmd = payload["sigtype"]; - LLNotificationSet::iterator foundItem = mItems.find(pNotification); - bool wasFound = (foundItem != mItems.end()); - bool passesFilter = mFilter ? mFilter(pNotification) : true; - - // first, we offer the result of the filter test to the simple - // signals for pass/fail. One of these is guaranteed to be called. - // If either signal returns true, the change processing is NOT performed - // (so don't return true unless you know what you're doing!) - bool abortProcessing = false; - if (passesFilter) - { - onFilterPass(pNotification); - abortProcessing = mPassedFilter(payload); - } - else - { - onFilterFail(pNotification); - abortProcessing = mFailedFilter(payload); - } - - if (abortProcessing) - { - return true; - } - - if (cmd == "load") - { - // should be no reason we'd ever get a load if we already have it - // if passes filter send a load message, else do nothing - assert(!wasFound); - if (passesFilter) - { - // not in our list, add it and say so - mItems.insert(pNotification); - onLoad(pNotification); - abortProcessing = mChanged(payload); - } - } - else if (cmd == "change") - { - // if it passes filter now and was found, we just send a change message - // if it passes filter now and wasn't found, we have to add it - // if it doesn't pass filter and wasn't found, we do nothing - // if it doesn't pass filter and was found, we need to delete it - if (passesFilter) - { - if (wasFound) - { - // it already existed, so this is a change - // since it changed in place, all we have to do is resend the signal - onChange(pNotification); - abortProcessing = mChanged(payload); - } - else - { - // not in our list, add it and say so - mItems.insert(pNotification); - onChange(pNotification); - // our payload is const, so make a copy before changing it - LLSD newpayload = payload; - newpayload["sigtype"] = "add"; - abortProcessing = mChanged(newpayload); - } - } - else - { - if (wasFound) - { - // it already existed, so this is a delete - mItems.erase(pNotification); - onChange(pNotification); - // our payload is const, so make a copy before changing it - LLSD newpayload = payload; - newpayload["sigtype"] = "delete"; - abortProcessing = mChanged(newpayload); - } - // didn't pass, not on our list, do nothing - } - } - else if (cmd == "add") - { - // should be no reason we'd ever get an add if we already have it - // if passes filter send an add message, else do nothing - assert(!wasFound); - if (passesFilter) - { - // not in our list, add it and say so - mItems.insert(pNotification); - onAdd(pNotification); - abortProcessing = mChanged(payload); - } - } - else if (cmd == "delete") - { - // if we have it in our list, pass on the delete, then delete it, else do nothing - if (wasFound) - { - onDelete(pNotification); - abortProcessing = mChanged(payload); - mItems.erase(pNotification); - } - } - return abortProcessing; -} - -LLNotificationChannel::LLNotificationChannel(const Params& p) -: LLNotificationChannelBase(p.filter()), - LLInstanceTracker(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()), - mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()) -{ - for (const std::string& source : p.sources) - { - connectToChannel(source); - } -} - - -LLNotificationChannel::LLNotificationChannel(const std::string& name, - const std::string& parent, - LLNotificationFilter filter) -: LLNotificationChannelBase(filter), - LLInstanceTracker(name), - mName(name) -{ - // bind to notification broadcast - connectToChannel(parent); -} - -LLNotificationChannel::~LLNotificationChannel() -{ - for (LLBoundListener &listener : mListeners) - { - listener.disconnect(); - } -} - -bool LLNotificationChannel::isEmpty() const -{ - return mItems.empty(); -} - -S32 LLNotificationChannel::size() const -{ - return mItems.size(); -} - -size_t LLNotificationChannel::size() -{ - return mItems.size(); -} - -void LLNotificationChannel::forEachNotification(NotificationProcess process) -{ - LLMutexLock lock(&mItemsMutex); - std::for_each(mItems.begin(), mItems.end(), process); -} - -std::string LLNotificationChannel::summarize() -{ - std::string s("Channel '"); - s += mName; - s += "'\n "; - LLMutexLock lock(&mItemsMutex); - for (LLNotificationChannel::Iterator it = mItems.begin(); it != mItems.end(); ++it) - { - s += (*it)->summarize(); - s += "\n "; - } - return s; -} - -void LLNotificationChannel::connectToChannel( const std::string& channel_name ) -{ - if (channel_name.empty()) - { - mListeners.push_back(LLNotifications::instance().connectChanged( - boost::bind(&LLNotificationChannelBase::updateItem, this, _1))); - } - else - { - mParents.push_back(channel_name); - LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name); - mListeners.push_back(p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1))); - } -} - -// --- -// END OF LLNotificationChannel implementation -// ========================================================= - - -// ============================================== =========== -// LLNotifications implementation -// --- -LLNotifications::LLNotifications() -: LLNotificationChannelBase(LLNotificationFilters::includeEverything), - mIgnoreAllNotifications(false) -{ - mListener.reset(new LLNotificationsListener(*this)); - LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); - - // touch the instance tracker for notification channels, so that it will still be around in our destructor - LLInstanceTracker::instanceCount(); -} - -void LLNotifications::clear() -{ - mDefaultChannels.clear(); -} - -// The expiration channel gets all notifications that are cancelled -bool LLNotifications::expirationFilter(LLNotificationPtr pNotification) -{ - return pNotification->isCancelled() || pNotification->isRespondedTo(); -} - -bool LLNotifications::expirationHandler(const LLSD& payload) -{ - if (payload["sigtype"].asString() != "delete") - { - // anything added to this channel actually should be deleted from the master - cancel(find(payload["id"])); - return true; // don't process this item any further - } - return false; -} - -bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif) -{ - if (!pNotif->hasUniquenessConstraints()) - { - return true; - } - - // checks against existing unique notifications - for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); - existing_it != mUniqueNotifications.end(); - ++existing_it) - { - LLNotificationPtr existing_notification = existing_it->second; - if (pNotif != existing_notification - && pNotif->isEquivalentTo(existing_notification)) - { - if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD) - { - cancel(existing_notification); - return true; - } - else - { - return false; - } - } - } - - return true; -} - -bool LLNotifications::uniqueHandler(const LLSD& payload) -{ - std::string cmd = payload["sigtype"]; - - LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); - if (pNotif && pNotif->hasUniquenessConstraints()) - { - if (cmd == "add") - { - // not a duplicate according to uniqueness criteria, so we keep it - // and store it for future uniqueness checks - mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif)); - } - else if (cmd == "delete") - { - mUniqueNotifications.erase(pNotif->getName()); - } - } - - return false; -} - -bool LLNotifications::failedUniquenessTest(const LLSD& payload) -{ - LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); - - std::string cmd = payload["sigtype"]; - - if (!pNotif || cmd != "add") - { - return false; - } - - switch(pNotif->getCombineBehavior()) - { - case LLNotification::REPLACE_WITH_NEW: - // Update the existing unique notification with the data from this particular instance... - // This guarantees that duplicate notifications will be collapsed to the one - // most recently triggered - for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); - existing_it != mUniqueNotifications.end(); - ++existing_it) - { - LLNotificationPtr existing_notification = existing_it->second; - if (pNotif != existing_notification - && pNotif->isEquivalentTo(existing_notification)) - { - // copy notification instance data over to oldest instance - // of this unique notification and update it - existing_notification->updateFrom(pNotif); - // then delete the new one - cancel(pNotif); - } - } - break; - case LLNotification::COMBINE_WITH_NEW: - // Add to the existing unique notification with the data from this particular instance... - // This guarantees that duplicate notifications will be collapsed to the one - // most recently triggered - for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); - existing_it != mUniqueNotifications.end(); - ++existing_it) - { - LLNotificationPtr existing_notification = existing_it->second; - if (pNotif != existing_notification - && pNotif->isEquivalentTo(existing_notification)) - { - // copy the notifications from the newest instance into the oldest - existing_notification->mCombinedNotifications.push_back(pNotif); - existing_notification->mCombinedNotifications.insert(existing_notification->mCombinedNotifications.end(), - pNotif->mCombinedNotifications.begin(), pNotif->mCombinedNotifications.end()); - - // pop up again - existing_notification->update(); - } - } - break; - case LLNotification::KEEP_OLD: - break; - case LLNotification::CANCEL_OLD: - // already handled by filter logic - break; - default: - break; - } - - return false; -} - -LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName) -{ - return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName).get()); -} - - -// this function is called once at construction time, after the object is constructed. -void LLNotifications::initSingleton() -{ - loadTemplates(); - loadVisibilityRules(); - createDefaultChannels(); -} - -void LLNotifications::cleanupSingleton() -{ - clear(); -} - -void LLNotifications::createDefaultChannels() -{ - LL_INFOS("Notifications") << "Generating default notification channels" << LL_ENDL; - // now construct the various channels AFTER loading the notifications, - // because the history channel is going to rewrite the stored notifications file - mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "", - !boost::bind(&LLNotifications::getIgnoreAllNotifications, this))); - mDefaultChannels.push_back(new LLNotificationChannel("Expiration", "Enabled", - boost::bind(&LLNotifications::expirationFilter, this, _1))); - mDefaultChannels.push_back(new LLNotificationChannel("Unexpired", "Enabled", - !boost::bind(&LLNotifications::expirationFilter, this, _1))); // use negated bind - mDefaultChannels.push_back(new LLNotificationChannel("Unique", "Unexpired", - boost::bind(&LLNotifications::uniqueFilter, this, _1))); - mDefaultChannels.push_back(new LLNotificationChannel("Ignore", "Unique", - filterIgnoredNotifications)); - mDefaultChannels.push_back(new LLNotificationChannel("VisibilityRules", "Ignore", - boost::bind(&LLNotifications::isVisibleByRules, this, _1))); - mDefaultChannels.push_back(new LLNotificationChannel("Visible", "VisibilityRules", - &LLNotificationFilters::includeEverything)); - mDefaultChannels.push_back(new LLPersistentNotificationChannel()); - - // connect action methods to these channels - getChannel("Enabled")->connectFailedFilter(&defaultResponse); - getChannel("Expiration")->connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1)); - // uniqueHandler slot should be added as first slot of the signal due to - // usage LLStopWhenHandled combiner in LLStandardSignal - getChannel("Unique")->connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1)); - getChannel("Unique")->connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1)); - getChannel("Ignore")->connectFailedFilter(&handleIgnoredNotification); - getChannel("VisibilityRules")->connectFailedFilter(&visibilityRuleMached); -} - - -LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name) -{ - if (mTemplates.count(name)) - { - return mTemplates[name]; - } - else - { - return mTemplates["MissingAlert"]; - } -} - -bool LLNotifications::templateExists(const std::string& name) -{ - return (mTemplates.count(name) != 0); -} - -void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option) -{ - LLNotificationPtr temp_notify(new LLNotification(params)); - LLSD response = temp_notify->getResponseTemplate(); - LLSD selected_item = temp_notify->getForm()->getElement(option); - - if (selected_item.isUndefined()) - { - LL_WARNS("Notifications") << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL; - return; - } - response[selected_item["name"].asString()] = true; - - temp_notify->respond(response); -} - -LLNotifications::TemplateNames LLNotifications::getTemplateNames() const -{ - TemplateNames names; - for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it) - { - names.push_back(it->first); - } - return names; -} - -typedef std::map StringMap; -void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements) -{ - // walk the list of attributes looking for replacements - for (LLXMLAttribList::iterator it=node->mAttributes.begin(); - it != node->mAttributes.end(); ++it) - { - std::string value = it->second->getValue(); - if (value[0] == '$') - { - value.erase(0, 1); // trim off the $ - std::string replacement; - StringMap::const_iterator found = replacements.find(value); - if (found != replacements.end()) - { - replacement = found->second; - LL_DEBUGS("Notifications") << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL; - it->second->setValue(replacement); - } - else - { - LL_WARNS("Notifications") << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL; - } - } - } - - // now walk the list of children and call this recursively. - for (LLXMLNodePtr child = node->getFirstChild(); - child.notNull(); child = child->getNextSibling()) - { - replaceSubstitutionStrings(child, replacements); - } -} - -void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace) -{ - if (form.ignore.isProvided() && form.ignore.text() == pattern) - { - form.ignore.text = replace; - } - - for (LLNotificationForm::FormElement& element : form.form_elements.elements) - { - if (element.button.isChosen() && element.button.text() == pattern) - { - element.button.text = replace; - } - } -} - -void addPathIfExists(const std::string& new_path, std::vector& paths) -{ - if (gDirUtilp->fileExists(new_path)) - { - paths.push_back(new_path); - } -} - -bool LLNotifications::loadTemplates() -{ - LL_INFOS("Notifications") << "Reading notifications template" << LL_ENDL; - // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it - // output all relevant pathnames instead of just the ones from the most - // specific skin. - std::vector search_paths = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS); - if (search_paths.empty()) - { - LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); - LL_ERRS() << "Problem finding notifications.xml" << LL_ENDL; - } - - std::string base_filename = search_paths.front(); - LLXMLNodePtr root; - bool success = LLXMLNode::getLayeredXMLNode(root, search_paths); - - if (!success || root.isNull() || !root->hasName( "notifications" )) - { - LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); - LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL; - return false; - } - - LLNotificationTemplate::Notifications params; - LLXUIParser parser; - parser.readXUI(root, params, base_filename); - - if(!params.validateBlock()) - { - LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); - LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL; - return false; - } - - mTemplates.clear(); - - for (const LLNotificationTemplate::GlobalString& string : params.strings) - { - mGlobalStrings[string.name] = string.value; - } - - std::map form_templates; - - for (const LLNotificationTemplate::Template& notification_template : params.templates) - { - form_templates[notification_template.name] = notification_template.form; - } - - for (LLNotificationTemplate::Params& notification : params.notifications) - { - if (notification.form_ref.form_template.isChosen()) - { - // replace form contents from template - notification.form_ref.form = form_templates[notification.form_ref.form_template.name]; - if(notification.form_ref.form_template.yes_text.isProvided()) - { - replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text); - } - if(notification.form_ref.form_template.no_text.isProvided()) - { - replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text); - } - if(notification.form_ref.form_template.cancel_text.isProvided()) - { - replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text); - } - if(notification.form_ref.form_template.help_text.isProvided()) - { - replaceFormText(notification.form_ref.form, "$helptext", notification.form_ref.form_template.help_text); - } - if(notification.form_ref.form_template.ignore_text.isProvided()) - { - replaceFormText(notification.form_ref.form, "$ignoretext", notification.form_ref.form_template.ignore_text); - } - } - mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification)); - } - - LL_INFOS("Notifications") << "...done" << LL_ENDL; - - return true; -} - -bool LLNotifications::loadVisibilityRules() -{ - const std::string xml_filename = "notification_visibility.xml"; - // Note that here we're looking for the "en" version, the default - // language, rather than the most localized version of this file. - std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename); - - LLNotificationVisibilityRule::Rules params; - LLSimpleXUIParser parser; - parser.readXUI(full_filename, params); - - if(!params.validateBlock()) - { - LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); - LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL; - return false; - } - - mVisibilityRules.clear(); - - for (const LLNotificationVisibilityRule::Rule& rule : params.rules) - { - mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule))); - } - - return true; -} - -// Add a simple notification (from XUI) -void LLNotifications::addFromCallback(const LLSD& name) -{ - add(name.asString(), LLSD(), LLSD()); -} - -LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload) -{ - LLNotification::Params::Functor functor_p; - functor_p.name = name; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); -} - -LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, const std::string& functor_name) -{ - LLNotification::Params::Functor functor_p; - functor_p.name = functor_name; - return add(LLNotification::Params().name(name) - .substitutions(substitutions) - .payload(payload) - .functor(functor_p)); -} - -//virtual -LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor) -{ - LLNotification::Params::Functor functor_p; - functor_p.function = functor; - return add(LLNotification::Params().name(name) - .substitutions(substitutions) - .payload(payload) - .functor(functor_p)); -} - -// generalized add function that takes a parameter block object for more complex instantiations -LLNotificationPtr LLNotifications::add(const LLNotification::Params& p) -{ - LLNotificationPtr pNotif(new LLNotification(p)); - add(pNotif); - return pNotif; -} - - -void LLNotifications::add(const LLNotificationPtr pNotif) -{ - if (pNotif == NULL) return; - - // first see if we already have it -- if so, that's a problem - LLNotificationSet::iterator it=mItems.find(pNotif); - if (it != mItems.end()) - { - LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL; - } - - updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif); -} - -void LLNotifications::load(const LLNotificationPtr pNotif) -{ - if (pNotif == NULL) return; - - // first see if we already have it -- if so, that's a problem - LLNotificationSet::iterator it=mItems.find(pNotif); - if (it != mItems.end()) - { - LL_ERRS() << "Notification loaded a second time to the master notification channel." << LL_ENDL; - } - - updateItem(LLSD().with("sigtype", "load").with("id", pNotif->id()), pNotif); -} - -void LLNotifications::cancel(LLNotificationPtr pNotif) -{ - if (pNotif == NULL || pNotif->isCancelled()) return; - - LLNotificationSet::iterator it=mItems.find(pNotif); - if (it != mItems.end()) - { - pNotif->cancel(); - updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); - } -} - -void LLNotifications::cancelByName(const std::string& name) -{ - LLMutexLock lock(&mItemsMutex); - std::vector notifs_to_cancel; - for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end(); - it != end_it; - ++it) - { - LLNotificationPtr pNotif = *it; - if (pNotif->getName() == name) - { - notifs_to_cancel.push_back(pNotif); - } - } - - for (std::vector::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end(); - it != end_it; - ++it) - { - LLNotificationPtr pNotif = *it; - pNotif->cancel(); - updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); - } -} - -void LLNotifications::cancelByOwner(const LLUUID ownerId) -{ - LLMutexLock lock(&mItemsMutex); - std::vector notifs_to_cancel; - for (LLNotificationSet::iterator it = mItems.begin(), end_it = mItems.end(); - it != end_it; - ++it) - { - LLNotificationPtr pNotif = *it; - if (pNotif && pNotif->getPayload().get("owner_id").asUUID() == ownerId) - { - notifs_to_cancel.push_back(pNotif); - } - } - - for (std::vector::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end(); - it != end_it; - ++it) - { - LLNotificationPtr pNotif = *it; - pNotif->cancel(); - updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); - } -} - -void LLNotifications::update(const LLNotificationPtr pNotif) -{ - LLNotificationSet::iterator it=mItems.find(pNotif); - if (it != mItems.end()) - { - updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif); - } -} - - -LLNotificationPtr LLNotifications::find(LLUUID uuid) -{ - LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid))); - LLNotificationSet::iterator it=mItems.find(target); - if (it == mItems.end()) - { - LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL; - return LLNotificationPtr((LLNotification*)NULL); - } - else - { - return *it; - } -} - -std::string LLNotifications::getGlobalString(const std::string& key) const -{ - GlobalStringMap::const_iterator it = mGlobalStrings.find(key); - if (it != mGlobalStrings.end()) - { - return it->second; - } - else - { - // if we don't have the key as a global, return the key itself so that the error - // is self-diagnosing. - return key; - } -} - -void LLNotifications::setIgnoreAllNotifications(bool setting) -{ - mIgnoreAllNotifications = setting; -} -bool LLNotifications::getIgnoreAllNotifications() -{ - return mIgnoreAllNotifications; -} - -void LLNotifications::setIgnored(const std::string& name, bool ignored) -{ - LLNotificationTemplatePtr templatep = getTemplate(name); - templatep->mForm->setIgnored(ignored); -} - -bool LLNotifications::getIgnored(const std::string& name) -{ - LLNotificationTemplatePtr templatep = getTemplate(name); - return (mIgnoreAllNotifications) || ( (templatep->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) && (templatep->mForm->getIgnored()) ); -} - -bool LLNotifications::isVisibleByRules(LLNotificationPtr n) -{ - if(n->isRespondedTo()) - { - // This avoids infinite recursion in the case where the filter calls respond() - return true; - } - - VisibilityRuleList::iterator it; - - for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++) - { - // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule. - LL_DEBUGS("Notifications") - << "notification \"" << n->getName() << "\" " - << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, " - << "name = \"" << (*it)->mName << "\" " - << "tag = \"" << (*it)->mTag << "\" " - << "type = \"" << (*it)->mType << "\" " - << LL_ENDL; - - if(!(*it)->mType.empty()) - { - if((*it)->mType != n->getType()) - { - // Type doesn't match, so skip this rule. - continue; - } - } - - if(!(*it)->mTag.empty()) - { - // check this notification's tag(s) against it->mTag and continue if no match is found. - if(!n->matchesTag((*it)->mTag)) - { - // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule. - continue; - } - } - - if(!(*it)->mName.empty()) - { - // check this notification's name against the notification's name and continue if no match is found. - if((*it)->mName != n->getName()) - { - // This rule's non-empty name didn't match the notification. Skip this rule. - continue; - } - } - - // If we got here, the rule matches. Don't evaluate subsequent rules. - if(!(*it)->mVisible) - { - // This notification is being hidden. - - if((*it)->mResponse.empty()) - { - // Response property is empty. Cancel this notification. - LL_DEBUGS("Notifications") << "cancelling notification " << n->getName() << LL_ENDL; - - cancel(n); - } - else - { - // Response property is not empty. Return the specified response. - LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON); - // TODO: verify that the response template has an item with the correct name - response[(*it)->mResponse] = true; - - LL_DEBUGS("Notifications") << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL; - - n->respond(response); - } - - return false; - } - - // If we got here, exit the loop and return true. - break; - } - - LL_DEBUGS("Notifications") << "allowing notification " << n->getName() << LL_ENDL; - - return true; -} - - -// --- -// END OF LLNotifications implementation -// ========================================================= - -std::ostream& operator<<(std::ostream& s, const LLNotification& notification) -{ - s << notification.summarize(); - return s; -} - -void LLPostponedNotification::lookupName(const LLUUID& id, - bool is_group) -{ - if (is_group) - { - gCacheName->getGroup(id, - boost::bind(&LLPostponedNotification::onGroupNameCache, - this, _1, _2, _3)); - } - else - { - fetchAvatarName(id); - } -} - -void LLPostponedNotification::onGroupNameCache(const LLUUID& id, - const std::string& full_name, - bool is_group) -{ - finalizeName(full_name); -} - -void LLPostponedNotification::fetchAvatarName(const LLUUID& id) -{ - if (id.notNull()) - { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - - mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLPostponedNotification::onAvatarNameCache, this, _1, _2)); - } -} - -void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id, - const LLAvatarName& av_name) -{ - mAvatarNameCacheConnection.disconnect(); - - std::string name = av_name.getCompleteName(); - - // from PE merge - we should figure out if this is the right thing to do - if (name.empty()) - { - LL_WARNS("Notifications") << "Empty name received for Id: " << agent_id << LL_ENDL; - name = SYSTEM_FROM; - } - - finalizeName(name); -} - -void LLPostponedNotification::finalizeName(const std::string& name) -{ - mName = name; - modifyNotificationParams(); - LLNotifications::instance().add(mParams); - cleanup(); -} +/** +* @file llnotifications.cpp +* @brief Non-UI queue manager for keeping a prioritized list of notifications +* +* $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 "llnotifications.h" +#include "llnotificationtemplate.h" +#include "llnotificationvisibilityrule.h" + +#include "llavatarnamecache.h" +#include "llinstantmessage.h" +#include "llcachename.h" +#include "llxmlnode.h" +#include "lluictrl.h" +#include "lluictrlfactory.h" +#include "lldir.h" +#include "llsdserialize.h" +#include "lltrans.h" +#include "llstring.h" +#include "llsdparam.h" +#include "llsdutil.h" + +#include +#include + + +const std::string NOTIFICATION_PERSIST_VERSION = "0.93"; + +void NotificationPriorityValues::declareValues() +{ + declare("low", NOTIFICATION_PRIORITY_LOW); + declare("normal", NOTIFICATION_PRIORITY_NORMAL); + declare("high", NOTIFICATION_PRIORITY_HIGH); + declare("critical", NOTIFICATION_PRIORITY_CRITICAL); +} + +LLNotificationForm::FormElementBase::FormElementBase() +: name("name"), + enabled("enabled", true) +{} + +LLNotificationForm::FormIgnore::FormIgnore() +: text("text"), + control("control"), + invert_control("invert_control", false), + save_option("save_option", false), + session_only("session_only", false), + checkbox_only("checkbox_only", false) +{} + +LLNotificationForm::FormButton::FormButton() +: index("index"), + text("text"), + ignore("ignore"), + is_default("default"), + width("width", 0), + type("type") +{ + // set type here so it gets serialized + type = "button"; +} + +LLNotificationForm::FormInput::FormInput() +: type("type"), + text("text"), + max_length_chars("max_length_chars"), + allow_emoji("allow_emoji"), + width("width", 0), + value("value") +{} + +LLNotificationForm::FormElement::FormElement() +: button("button"), + input("input") +{} + +LLNotificationForm::FormElements::FormElements() +: elements("") +{} + +LLNotificationForm::Params::Params() +: name("name"), + ignore("ignore"), + form_elements("") +{} + + + +bool filterIgnoredNotifications(LLNotificationPtr notification) +{ + LLNotificationFormPtr form = notification->getForm(); + // Check to see if the user wants to ignore this alert + return !notification->getForm()->getIgnored(); +} + +bool handleIgnoredNotification(const LLSD& payload) +{ + if (payload["sigtype"].asString() == "add") + { + LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); + if (!pNotif) return false; + + LLNotificationFormPtr form = pNotif->getForm(); + LLSD response; + switch(form->getIgnoreType()) + { + case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE: + case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY: + response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON); + break; + case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE: + response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName()); + break; + case LLNotificationForm::IGNORE_SHOW_AGAIN: + break; + default: + return false; + } + pNotif->setIgnored(true); + pNotif->respond(response); + return true; // don't process this item any further + } + return false; +} + +bool defaultResponse(const LLSD& payload) +{ + if (payload["sigtype"].asString() == "add") + { + LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); + if (pNotif) + { + // supply default response + pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON)); + } + } + return false; +} + +bool visibilityRuleMached(const LLSD& payload) +{ + // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification. + // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order. + return true; +} + + +namespace LLNotificationFilters +{ + // a sample filter + bool includeEverything(LLNotificationPtr p) + { + return true; + } +}; + +LLNotificationForm::LLNotificationForm() +: mIgnore(IGNORE_NO) +{ +} + +LLNotificationForm::LLNotificationForm( const LLNotificationForm& other ) +{ + mFormData = other.mFormData; + mIgnore = other.mIgnore; + mIgnoreMsg = other.mIgnoreMsg; + mIgnoreSetting = other.mIgnoreSetting; + mInvertSetting = other.mInvertSetting; +} + +LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p) +: mIgnore(IGNORE_NO), + mInvertSetting(false) // ignore settings by default mean true=show, false=ignore +{ + if (p.ignore.isProvided()) + { + // For all cases but IGNORE_CHECKBOX_ONLY this is name for use in preferences + mIgnoreMsg = p.ignore.text; + + LLUI *ui_inst = LLUI::getInstance(); + if (p.ignore.checkbox_only) + { + mIgnore = IGNORE_CHECKBOX_ONLY; + } + else if (!p.ignore.save_option) + { + mIgnore = p.ignore.session_only ? IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY : IGNORE_WITH_DEFAULT_RESPONSE; + } + else + { + // remember last option chosen by user and automatically respond with that in the future + mIgnore = IGNORE_WITH_LAST_RESPONSE; + ui_inst->mSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name)); + } + + bool show_notification = true; + if (p.ignore.control.isProvided()) + { + mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control()); + mInvertSetting = p.ignore.invert_control; + } + else if (mIgnore > IGNORE_NO) + { + ui_inst->mSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT); + mIgnoreSetting = ui_inst->mSettingGroups["ignores"]->getControl(name); + } + } + + LLParamSDParser parser; + parser.writeSD(mFormData, p.form_elements); + + for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray(); + it != end_it; + ++it) + { + // lift contents of form element up a level, since element type is already encoded in "type" param + if (it->isMap() && it->beginMap() != it->endMap()) + { + *it = it->beginMap()->second; + } + } + + LL_DEBUGS("Notifications") << name << LL_ENDL; + LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL; +} + +LLNotificationForm::LLNotificationForm(const LLSD& sd) + : mIgnore(IGNORE_NO) +{ + if (sd.isArray()) + { + mFormData = sd; + } + else + { + LL_WARNS("Notifications") << "Invalid form data " << sd << LL_ENDL; + mFormData = LLSD::emptyArray(); + } +} + +LLSD LLNotificationForm::asLLSD() const +{ + return mFormData; +} + +LLSD LLNotificationForm::getElement(const std::string& element_name) +{ + for (LLSD::array_const_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) return (*it); + } + return LLSD(); +} + + +bool LLNotificationForm::hasElement(const std::string& element_name) const +{ + for (LLSD::array_const_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) return true; + } + return false; +} + +void LLNotificationForm::getElements(LLSD& elements, S32 offset) +{ + //Finds elements that the template did not add + LLSD::array_const_iterator it = mFormData.beginArray() + offset; + + //Keeps track of only the dynamic elements + for(; it != mFormData.endArray(); ++it) + { + elements.append(*it); + } +} + +bool LLNotificationForm::getElementEnabled(const std::string& element_name) const +{ + for (LLSD::array_const_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) + { + return (*it)["enabled"].asBoolean(); + } + } + + return false; +} + +void LLNotificationForm::setElementEnabled(const std::string& element_name, bool enabled) +{ + for (LLSD::array_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) + { + (*it)["enabled"] = enabled; + } + } +} + + +void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value, bool enabled) +{ + LLSD element; + element["type"] = type; + element["name"] = name; + element["text"] = name; + element["value"] = value; + element["index"] = LLSD::Integer(mFormData.size()); + element["enabled"] = enabled; + mFormData.append(element); +} + +void LLNotificationForm::append(const LLSD& sub_form) +{ + if (sub_form.isArray()) + { + for (LLSD::array_const_iterator it = sub_form.beginArray(); + it != sub_form.endArray(); + ++it) + { + mFormData.append(*it); + } + } +} + +void LLNotificationForm::formatElements(const LLSD& substitutions) +{ + for (LLSD::array_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + // format "text" component of each form element + if ((*it).has("text")) + { + std::string text = (*it)["text"].asString(); + LLStringUtil::format(text, substitutions); + (*it)["text"] = text; + } + if ((*it)["type"].asString() == "text" && (*it).has("value")) + { + std::string value = (*it)["value"].asString(); + LLStringUtil::format(value, substitutions); + (*it)["value"] = value; + } + } +} + +std::string LLNotificationForm::getDefaultOption() +{ + for (LLSD::array_const_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["default"]) return (*it)["name"].asString(); + } + return ""; +} + +LLControlVariablePtr LLNotificationForm::getIgnoreSetting() +{ + return mIgnoreSetting; +} + +bool LLNotificationForm::getIgnored() +{ + bool show = true; + if (mIgnore > LLNotificationForm::IGNORE_NO + && mIgnoreSetting) + { + show = mIgnoreSetting->getValue().asBoolean(); + if (mInvertSetting) show = !show; + } + return !show; +} + +void LLNotificationForm::setIgnored(bool ignored) +{ + if (mIgnoreSetting) + { + if (mInvertSetting) ignored = !ignored; + mIgnoreSetting->setValue(!ignored); + } +} + +LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p) +: mName(p.name), + mType(p.type), + mMessage(p.value), + mFooter(p.footer.value), + mLabel(p.label), + mIcon(p.icon), + mURL(p.url.value), + mExpireSeconds(p.duration), + mExpireOption(p.expire_option), + mURLOption(p.url.option), + mURLTarget(p.url.target), + mForceUrlsExternal(p.force_urls_external), + mUnique(p.unique.isProvided()), + mCombineBehavior(p.unique.combine), + mPriority(p.priority), + mPersist(p.persist), + mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()), + mLogToChat(p.log_to_chat), + mLogToIM(p.log_to_im), + mShowToast(p.show_toast), + mFadeToast(p.fade_toast), + mSoundName("") +{ + if (p.sound.isProvided() + && LLUI::getInstance()->mSettingGroups["config"]->controlExists(p.sound)) + { + mSoundName = p.sound; + } + + for (const LLNotificationTemplate::UniquenessContext& context : p.unique.contexts) + { + mUniqueContext.push_back(context.value); + } + + LL_DEBUGS("Notifications") << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL; + + for (const LLNotificationTemplate::Tag& tag : p.tags) + { + LL_DEBUGS("Notifications") << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL; + mTags.push_back(tag.value); + } + + mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form)); +} + +LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p) +{ + if (p.show.isChosen()) + { + mType = p.show.type; + mTag = p.show.tag; + mName = p.show.name; + mVisible = true; + } + else if (p.hide.isChosen()) + { + mType = p.hide.type; + mTag = p.hide.tag; + mName = p.hide.name; + mVisible = false; + } + else if (p.respond.isChosen()) + { + mType = p.respond.type; + mTag = p.respond.tag; + mName = p.respond.name; + mVisible = false; + mResponse = p.respond.response; + } +} + +LLNotification::LLNotification(const LLSDParamAdapter& p) : + mTimestamp(p.time_stamp), + mSubstitutions(p.substitutions), + mPayload(p.payload), + mExpiresAt(p.expiry), + mTemporaryResponder(false), + mRespondedTo(false), + mPriority(p.priority), + mCancelled(false), + mIgnored(false), + mResponderObj(NULL), + mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()), + mOfferFromAgent(p.offer_from_agent), + mIsDND(p.is_dnd) +{ + if (p.functor.name.isChosen()) + { + mResponseFunctorName = p.functor.name; + } + else if (p.functor.function.isChosen()) + { + mResponseFunctorName = LLUUID::generateNewID().asString(); + LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function()); + + mTemporaryResponder = true; + } + else if(p.functor.responder.isChosen()) + { + mResponder = p.functor.responder; + } + + if(p.responder.isProvided()) + { + mResponderObj = p.responder; + } + + init(p.name, p.form_elements); +} + + +LLSD LLNotification::asLLSD(bool excludeTemplateElements) +{ + LLParamSDParser parser; + + Params p; + p.id = mId; + p.name = mTemplatep->mName; + p.substitutions = mSubstitutions; + p.payload = mPayload; + p.time_stamp = mTimestamp; + p.expiry = mExpiresAt; + p.priority = mPriority; + + LLNotificationFormPtr templateForm = mTemplatep->mForm; + LLSD formElements = mForm->asLLSD(); + + //All form elements (dynamic or not) + if(!excludeTemplateElements) + { + p.form_elements = formElements; + } + //Only dynamic form elements (exclude template elements) + else if(templateForm->getNumElements() < formElements.size()) + { + LLSD dynamicElements; + //Offset to dynamic elements and store them + mForm->getElements(dynamicElements, templateForm->getNumElements()); + p.form_elements = dynamicElements; + } + + if(mResponder) + { + p.functor.responder_sd = mResponder->asLLSD(); + } + + if(!mResponseFunctorName.empty()) + { + p.functor.name = mResponseFunctorName; + } + + LLSD output; + parser.writeSD(output, p); + return output; +} + +void LLNotification::update() +{ + LLNotifications::instance().update(shared_from_this()); +} + +void LLNotification::updateFrom(LLNotificationPtr other) +{ + // can only update from the same notification type + if (mTemplatep != other->mTemplatep) return; + + // NOTE: do NOT change the ID, since it is the key to + // this given instance, just update all the metadata + //mId = other->mId; + + mPayload = other->mPayload; + mSubstitutions = other->mSubstitutions; + mTimestamp = other->mTimestamp; + mExpiresAt = other->mExpiresAt; + mCancelled = other->mCancelled; + mIgnored = other->mIgnored; + mPriority = other->mPriority; + mForm = other->mForm; + mResponseFunctorName = other->mResponseFunctorName; + mRespondedTo = other->mRespondedTo; + mResponse = other->mResponse; + mTemporaryResponder = other->mTemporaryResponder; + + update(); +} + +const LLNotificationFormPtr LLNotification::getForm() +{ + return mForm; +} + +void LLNotification::cancel() +{ + mCancelled = true; +} + +LLSD LLNotification::getResponseTemplate(EResponseTemplateType type) +{ + LLSD response = LLSD::emptyMap(); + for (S32 element_idx = 0; + element_idx < mForm->getNumElements(); + ++element_idx) + { + LLSD element = mForm->getElement(element_idx); + if (element.has("name")) + { + response[element["name"].asString()] = element["value"]; + } + + if ((type == WITH_DEFAULT_BUTTON) + && element["default"].asBoolean()) + { + response[element["name"].asString()] = true; + } + } + return response; +} + +//static +S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response) +{ + LLNotificationForm form(notification["form"]); + + for (S32 element_idx = 0; + element_idx < form.getNumElements(); + ++element_idx) + { + LLSD element = form.getElement(element_idx); + + // only look at buttons + if (element["type"].asString() == "button" + && response[element["name"].asString()].asBoolean()) + { + return element["index"].asInteger(); + } + } + + return -1; +} + +//static +std::string LLNotification::getSelectedOptionName(const LLSD& response) +{ + for (LLSD::map_const_iterator response_it = response.beginMap(); + response_it != response.endMap(); + ++response_it) + { + if (response_it->second.isBoolean() && response_it->second.asBoolean()) + { + return response_it->first; + } + } + return ""; +} + + +void LLNotification::respond(const LLSD& response) +{ + // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo() + mRespondedTo = true; + mResponse = response; + + if(mResponder) + { + mResponder->handleRespond(asLLSD(), response); + } + else if (!mResponseFunctorName.empty()) + { + // look up the functor + LLNotificationFunctorRegistry::ResponseFunctor functor = + LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName); + // and then call it + functor(asLLSD(), response); + } + else if (mCombinedNotifications.empty()) + { + // no registered responder + return; + } + + if (mTemporaryResponder) + { + LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); + mResponseFunctorName = ""; + mTemporaryResponder = false; + } + + if (mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO) + { + mForm->setIgnored(mIgnored); + if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) + { + LLUI::getInstance()->mSettingGroups["ignores"]->setLLSD("Default" + getName(), response); + } + } + + for (std::vector::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it) + { + if ((*it)) + { + (*it)->respond(response); + } + } + + update(); +} + +void LLNotification::respondWithDefault() +{ + respond(getResponseTemplate(WITH_DEFAULT_BUTTON)); +} + + +const std::string& LLNotification::getName() const +{ + return mTemplatep->mName; +} + +const std::string& LLNotification::getIcon() const +{ + return mTemplatep->mIcon; +} + + +bool LLNotification::isPersistent() const +{ + return mTemplatep->mPersist; +} + +std::string LLNotification::getType() const +{ + return (mTemplatep ? mTemplatep->mType : ""); +} + +S32 LLNotification::getURLOption() const +{ + return (mTemplatep ? mTemplatep->mURLOption : -1); +} + +S32 LLNotification::getURLOpenExternally() const +{ + return(mTemplatep? mTemplatep->mURLTarget == "_external": -1); +} + +bool LLNotification::getForceUrlsExternal() const +{ + return (mTemplatep ? mTemplatep->mForceUrlsExternal : false); +} + +bool LLNotification::hasUniquenessConstraints() const +{ + return (mTemplatep ? mTemplatep->mUnique : false); +} + +bool LLNotification::matchesTag(const std::string& tag) +{ + bool result = false; + + if(mTemplatep) + { + std::list::iterator it; + for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++) + { + if((*it) == tag) + { + result = true; + break; + } + } + } + + return result; +} + +void LLNotification::setIgnored(bool ignore) +{ + mIgnored = ignore; +} + +void LLNotification::setResponseFunctor(std::string const &responseFunctorName) +{ + if (mTemporaryResponder) + // get rid of the old one + LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); + mResponseFunctorName = responseFunctorName; + mTemporaryResponder = false; +} + +void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb) +{ + if(mTemporaryResponder) + { + LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); + } + + LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb); +} + +void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder) +{ + mResponder = responder; +} + +bool LLNotification::isEquivalentTo(LLNotificationPtr that) const +{ + if (this->mTemplatep->mName != that->mTemplatep->mName) + { + return false; // must have the same template name or forget it + } + if (this->mTemplatep->mUnique) + { + const LLSD& these_substitutions = this->getSubstitutions(); + const LLSD& those_substitutions = that->getSubstitutions(); + const LLSD& this_payload = this->getPayload(); + const LLSD& that_payload = that->getPayload(); + + // highlander bit sez there can only be one of these + for (std::vector::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end(); + it != end_it; + ++it) + { + // if templates differ in either substitution strings or payload with the given field name + // then they are considered inequivalent + // use of get() avoids converting the LLSD value to a map as the [] operator would + if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString() + || this_payload.get(*it).asString() != that_payload.get(*it).asString()) + { + return false; + } + } + return true; + } + + return false; +} + +void LLNotification::init(const std::string& template_name, const LLSD& form_elements) +{ + mTemplatep = LLNotifications::instance().getTemplate(template_name); + if (!mTemplatep) return; + + // add default substitutions + const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs(); + for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin(); + iter != default_args.end(); ++iter) + { + mSubstitutions[iter->first] = iter->second; + } + mSubstitutions["_URL"] = getURL(); + mSubstitutions["_NAME"] = template_name; + // TODO: something like this so that a missing alert is sensible: + //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions); + + mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm)); + mForm->append(form_elements); + + // apply substitution to form labels + mForm->formatElements(mSubstitutions); + + mIgnored = mForm->getIgnored(); + + LLDate rightnow = LLDate::now(); + if (mTemplatep->mExpireSeconds) + { + mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds); + } + + if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED) + { + mPriority = mTemplatep->mPriority; + } +} + +std::string LLNotification::summarize() const +{ + std::string s = "Notification("; + s += getName(); + s += ") : "; + s += mTemplatep ? mTemplatep->mMessage : ""; + // should also include timestamp and expiration time (but probably not payload) + return s; +} + +std::string LLNotification::getMessage() const +{ + // all our callers cache this result, so it gives us more flexibility + // to do the substitution at call time rather than attempting to + // cache it in the notification + if (!mTemplatep) + return std::string(); + + std::string message = mTemplatep->mMessage; + LLStringUtil::format(message, mSubstitutions); + return message; +} + +std::string LLNotification::getFooter() const +{ + if (!mTemplatep) + return std::string(); + + std::string footer = mTemplatep->mFooter; + LLStringUtil::format(footer, mSubstitutions); + return footer; +} + +std::string LLNotification::getLabel() const +{ + std::string label = mTemplatep->mLabel; + LLStringUtil::format(label, mSubstitutions); + return (mTemplatep ? label : ""); +} + +std::string LLNotification::getURL() const +{ + if (!mTemplatep) + return std::string(); + std::string url = mTemplatep->mURL; + LLStringUtil::format(url, mSubstitutions); + return (mTemplatep ? url : ""); +} + +bool LLNotification::canLogToChat() const +{ + return mTemplatep->mLogToChat; +} + +bool LLNotification::canLogToIM() const +{ + return mTemplatep->mLogToIM; +} + +bool LLNotification::canShowToast() const +{ + return mTemplatep->mShowToast; +} + +bool LLNotification::canFadeToast() const +{ + return mTemplatep->mFadeToast; +} + +bool LLNotification::hasFormElements() const +{ + return mTemplatep->mForm->getNumElements() != 0; +} + +void LLNotification::playSound() +{ + make_ui_sound(mTemplatep->mSoundName.c_str()); +} + +LLNotification::ECombineBehavior LLNotification::getCombineBehavior() const +{ + return mTemplatep->mCombineBehavior; +} + +void LLNotification::updateForm( const LLNotificationFormPtr& form ) +{ + mForm = form; +} + +void LLNotification::repost() +{ + mRespondedTo = false; + LLNotifications::instance().update(shared_from_this()); +} + + + +// ========================================================= +// LLNotificationChannel implementation +// --- +LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot) +{ + // when someone wants to connect to a channel, we first throw them + // all of the notifications that are already in the channel + // we use a special signal called "load" in case the channel wants to care + // only about new notifications + LLMutexLock lock(&mItemsMutex); + for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) + { + slot(LLSD().with("sigtype", "load").with("id", (*it)->id())); + } + // and then connect the signal so that all future notifications will also be + // forwarded. + return mChanged.connect(slot); +} + +LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot) +{ + for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) + { + slot(LLSD().with("sigtype", "load").with("id", (*it)->id())); + } + return mChanged.connect(slot, boost::signals2::at_front); +} + +LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot) +{ + // these two filters only fire for notifications added after the current one, because + // they don't participate in the hierarchy. + return mPassedFilter.connect(slot); +} + +LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot) +{ + return mFailedFilter.connect(slot); +} + +// external call, conforms to our standard signature +bool LLNotificationChannelBase::updateItem(const LLSD& payload) +{ + // first check to see if it's in the master list + LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]); + if (!pNotification) + return false; // not found + + return updateItem(payload, pNotification); +} + + +//FIX QUIT NOT WORKING + + +// internal call, for use in avoiding lookup +bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification) +{ + std::string cmd = payload["sigtype"]; + LLNotificationSet::iterator foundItem = mItems.find(pNotification); + bool wasFound = (foundItem != mItems.end()); + bool passesFilter = mFilter ? mFilter(pNotification) : true; + + // first, we offer the result of the filter test to the simple + // signals for pass/fail. One of these is guaranteed to be called. + // If either signal returns true, the change processing is NOT performed + // (so don't return true unless you know what you're doing!) + bool abortProcessing = false; + if (passesFilter) + { + onFilterPass(pNotification); + abortProcessing = mPassedFilter(payload); + } + else + { + onFilterFail(pNotification); + abortProcessing = mFailedFilter(payload); + } + + if (abortProcessing) + { + return true; + } + + if (cmd == "load") + { + // should be no reason we'd ever get a load if we already have it + // if passes filter send a load message, else do nothing + assert(!wasFound); + if (passesFilter) + { + // not in our list, add it and say so + mItems.insert(pNotification); + onLoad(pNotification); + abortProcessing = mChanged(payload); + } + } + else if (cmd == "change") + { + // if it passes filter now and was found, we just send a change message + // if it passes filter now and wasn't found, we have to add it + // if it doesn't pass filter and wasn't found, we do nothing + // if it doesn't pass filter and was found, we need to delete it + if (passesFilter) + { + if (wasFound) + { + // it already existed, so this is a change + // since it changed in place, all we have to do is resend the signal + onChange(pNotification); + abortProcessing = mChanged(payload); + } + else + { + // not in our list, add it and say so + mItems.insert(pNotification); + onChange(pNotification); + // our payload is const, so make a copy before changing it + LLSD newpayload = payload; + newpayload["sigtype"] = "add"; + abortProcessing = mChanged(newpayload); + } + } + else + { + if (wasFound) + { + // it already existed, so this is a delete + mItems.erase(pNotification); + onChange(pNotification); + // our payload is const, so make a copy before changing it + LLSD newpayload = payload; + newpayload["sigtype"] = "delete"; + abortProcessing = mChanged(newpayload); + } + // didn't pass, not on our list, do nothing + } + } + else if (cmd == "add") + { + // should be no reason we'd ever get an add if we already have it + // if passes filter send an add message, else do nothing + assert(!wasFound); + if (passesFilter) + { + // not in our list, add it and say so + mItems.insert(pNotification); + onAdd(pNotification); + abortProcessing = mChanged(payload); + } + } + else if (cmd == "delete") + { + // if we have it in our list, pass on the delete, then delete it, else do nothing + if (wasFound) + { + onDelete(pNotification); + abortProcessing = mChanged(payload); + mItems.erase(pNotification); + } + } + return abortProcessing; +} + +LLNotificationChannel::LLNotificationChannel(const Params& p) +: LLNotificationChannelBase(p.filter()), + LLInstanceTracker(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()), + mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()) +{ + for (const std::string& source : p.sources) + { + connectToChannel(source); + } +} + + +LLNotificationChannel::LLNotificationChannel(const std::string& name, + const std::string& parent, + LLNotificationFilter filter) +: LLNotificationChannelBase(filter), + LLInstanceTracker(name), + mName(name) +{ + // bind to notification broadcast + connectToChannel(parent); +} + +LLNotificationChannel::~LLNotificationChannel() +{ + for (LLBoundListener &listener : mListeners) + { + listener.disconnect(); + } +} + +bool LLNotificationChannel::isEmpty() const +{ + return mItems.empty(); +} + +S32 LLNotificationChannel::size() const +{ + return mItems.size(); +} + +size_t LLNotificationChannel::size() +{ + return mItems.size(); +} + +void LLNotificationChannel::forEachNotification(NotificationProcess process) +{ + LLMutexLock lock(&mItemsMutex); + std::for_each(mItems.begin(), mItems.end(), process); +} + +std::string LLNotificationChannel::summarize() +{ + std::string s("Channel '"); + s += mName; + s += "'\n "; + LLMutexLock lock(&mItemsMutex); + for (LLNotificationChannel::Iterator it = mItems.begin(); it != mItems.end(); ++it) + { + s += (*it)->summarize(); + s += "\n "; + } + return s; +} + +void LLNotificationChannel::connectToChannel( const std::string& channel_name ) +{ + if (channel_name.empty()) + { + mListeners.push_back(LLNotifications::instance().connectChanged( + boost::bind(&LLNotificationChannelBase::updateItem, this, _1))); + } + else + { + mParents.push_back(channel_name); + LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name); + mListeners.push_back(p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1))); + } +} + +// --- +// END OF LLNotificationChannel implementation +// ========================================================= + + +// ============================================== =========== +// LLNotifications implementation +// --- +LLNotifications::LLNotifications() +: LLNotificationChannelBase(LLNotificationFilters::includeEverything), + mIgnoreAllNotifications(false) +{ + mListener.reset(new LLNotificationsListener(*this)); + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); + + // touch the instance tracker for notification channels, so that it will still be around in our destructor + LLInstanceTracker::instanceCount(); +} + +void LLNotifications::clear() +{ + mDefaultChannels.clear(); +} + +// The expiration channel gets all notifications that are cancelled +bool LLNotifications::expirationFilter(LLNotificationPtr pNotification) +{ + return pNotification->isCancelled() || pNotification->isRespondedTo(); +} + +bool LLNotifications::expirationHandler(const LLSD& payload) +{ + if (payload["sigtype"].asString() != "delete") + { + // anything added to this channel actually should be deleted from the master + cancel(find(payload["id"])); + return true; // don't process this item any further + } + return false; +} + +bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif) +{ + if (!pNotif->hasUniquenessConstraints()) + { + return true; + } + + // checks against existing unique notifications + for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); + existing_it != mUniqueNotifications.end(); + ++existing_it) + { + LLNotificationPtr existing_notification = existing_it->second; + if (pNotif != existing_notification + && pNotif->isEquivalentTo(existing_notification)) + { + if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD) + { + cancel(existing_notification); + return true; + } + else + { + return false; + } + } + } + + return true; +} + +bool LLNotifications::uniqueHandler(const LLSD& payload) +{ + std::string cmd = payload["sigtype"]; + + LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); + if (pNotif && pNotif->hasUniquenessConstraints()) + { + if (cmd == "add") + { + // not a duplicate according to uniqueness criteria, so we keep it + // and store it for future uniqueness checks + mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif)); + } + else if (cmd == "delete") + { + mUniqueNotifications.erase(pNotif->getName()); + } + } + + return false; +} + +bool LLNotifications::failedUniquenessTest(const LLSD& payload) +{ + LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); + + std::string cmd = payload["sigtype"]; + + if (!pNotif || cmd != "add") + { + return false; + } + + switch(pNotif->getCombineBehavior()) + { + case LLNotification::REPLACE_WITH_NEW: + // Update the existing unique notification with the data from this particular instance... + // This guarantees that duplicate notifications will be collapsed to the one + // most recently triggered + for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); + existing_it != mUniqueNotifications.end(); + ++existing_it) + { + LLNotificationPtr existing_notification = existing_it->second; + if (pNotif != existing_notification + && pNotif->isEquivalentTo(existing_notification)) + { + // copy notification instance data over to oldest instance + // of this unique notification and update it + existing_notification->updateFrom(pNotif); + // then delete the new one + cancel(pNotif); + } + } + break; + case LLNotification::COMBINE_WITH_NEW: + // Add to the existing unique notification with the data from this particular instance... + // This guarantees that duplicate notifications will be collapsed to the one + // most recently triggered + for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); + existing_it != mUniqueNotifications.end(); + ++existing_it) + { + LLNotificationPtr existing_notification = existing_it->second; + if (pNotif != existing_notification + && pNotif->isEquivalentTo(existing_notification)) + { + // copy the notifications from the newest instance into the oldest + existing_notification->mCombinedNotifications.push_back(pNotif); + existing_notification->mCombinedNotifications.insert(existing_notification->mCombinedNotifications.end(), + pNotif->mCombinedNotifications.begin(), pNotif->mCombinedNotifications.end()); + + // pop up again + existing_notification->update(); + } + } + break; + case LLNotification::KEEP_OLD: + break; + case LLNotification::CANCEL_OLD: + // already handled by filter logic + break; + default: + break; + } + + return false; +} + +LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName) +{ + return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName).get()); +} + + +// this function is called once at construction time, after the object is constructed. +void LLNotifications::initSingleton() +{ + loadTemplates(); + loadVisibilityRules(); + createDefaultChannels(); +} + +void LLNotifications::cleanupSingleton() +{ + clear(); +} + +void LLNotifications::createDefaultChannels() +{ + LL_INFOS("Notifications") << "Generating default notification channels" << LL_ENDL; + // now construct the various channels AFTER loading the notifications, + // because the history channel is going to rewrite the stored notifications file + mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "", + !boost::bind(&LLNotifications::getIgnoreAllNotifications, this))); + mDefaultChannels.push_back(new LLNotificationChannel("Expiration", "Enabled", + boost::bind(&LLNotifications::expirationFilter, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Unexpired", "Enabled", + !boost::bind(&LLNotifications::expirationFilter, this, _1))); // use negated bind + mDefaultChannels.push_back(new LLNotificationChannel("Unique", "Unexpired", + boost::bind(&LLNotifications::uniqueFilter, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Ignore", "Unique", + filterIgnoredNotifications)); + mDefaultChannels.push_back(new LLNotificationChannel("VisibilityRules", "Ignore", + boost::bind(&LLNotifications::isVisibleByRules, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Visible", "VisibilityRules", + &LLNotificationFilters::includeEverything)); + mDefaultChannels.push_back(new LLPersistentNotificationChannel()); + + // connect action methods to these channels + getChannel("Enabled")->connectFailedFilter(&defaultResponse); + getChannel("Expiration")->connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1)); + // uniqueHandler slot should be added as first slot of the signal due to + // usage LLStopWhenHandled combiner in LLStandardSignal + getChannel("Unique")->connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1)); + getChannel("Unique")->connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1)); + getChannel("Ignore")->connectFailedFilter(&handleIgnoredNotification); + getChannel("VisibilityRules")->connectFailedFilter(&visibilityRuleMached); +} + + +LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name) +{ + if (mTemplates.count(name)) + { + return mTemplates[name]; + } + else + { + return mTemplates["MissingAlert"]; + } +} + +bool LLNotifications::templateExists(const std::string& name) +{ + return (mTemplates.count(name) != 0); +} + +void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option) +{ + LLNotificationPtr temp_notify(new LLNotification(params)); + LLSD response = temp_notify->getResponseTemplate(); + LLSD selected_item = temp_notify->getForm()->getElement(option); + + if (selected_item.isUndefined()) + { + LL_WARNS("Notifications") << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL; + return; + } + response[selected_item["name"].asString()] = true; + + temp_notify->respond(response); +} + +LLNotifications::TemplateNames LLNotifications::getTemplateNames() const +{ + TemplateNames names; + for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it) + { + names.push_back(it->first); + } + return names; +} + +typedef std::map StringMap; +void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements) +{ + // walk the list of attributes looking for replacements + for (LLXMLAttribList::iterator it=node->mAttributes.begin(); + it != node->mAttributes.end(); ++it) + { + std::string value = it->second->getValue(); + if (value[0] == '$') + { + value.erase(0, 1); // trim off the $ + std::string replacement; + StringMap::const_iterator found = replacements.find(value); + if (found != replacements.end()) + { + replacement = found->second; + LL_DEBUGS("Notifications") << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL; + it->second->setValue(replacement); + } + else + { + LL_WARNS("Notifications") << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL; + } + } + } + + // now walk the list of children and call this recursively. + for (LLXMLNodePtr child = node->getFirstChild(); + child.notNull(); child = child->getNextSibling()) + { + replaceSubstitutionStrings(child, replacements); + } +} + +void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace) +{ + if (form.ignore.isProvided() && form.ignore.text() == pattern) + { + form.ignore.text = replace; + } + + for (LLNotificationForm::FormElement& element : form.form_elements.elements) + { + if (element.button.isChosen() && element.button.text() == pattern) + { + element.button.text = replace; + } + } +} + +void addPathIfExists(const std::string& new_path, std::vector& paths) +{ + if (gDirUtilp->fileExists(new_path)) + { + paths.push_back(new_path); + } +} + +bool LLNotifications::loadTemplates() +{ + LL_INFOS("Notifications") << "Reading notifications template" << LL_ENDL; + // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it + // output all relevant pathnames instead of just the ones from the most + // specific skin. + std::vector search_paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS); + if (search_paths.empty()) + { + LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); + LL_ERRS() << "Problem finding notifications.xml" << LL_ENDL; + } + + std::string base_filename = search_paths.front(); + LLXMLNodePtr root; + bool success = LLXMLNode::getLayeredXMLNode(root, search_paths); + + if (!success || root.isNull() || !root->hasName( "notifications" )) + { + LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); + LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL; + return false; + } + + LLNotificationTemplate::Notifications params; + LLXUIParser parser; + parser.readXUI(root, params, base_filename); + + if(!params.validateBlock()) + { + LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); + LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL; + return false; + } + + mTemplates.clear(); + + for (const LLNotificationTemplate::GlobalString& string : params.strings) + { + mGlobalStrings[string.name] = string.value; + } + + std::map form_templates; + + for (const LLNotificationTemplate::Template& notification_template : params.templates) + { + form_templates[notification_template.name] = notification_template.form; + } + + for (LLNotificationTemplate::Params& notification : params.notifications) + { + if (notification.form_ref.form_template.isChosen()) + { + // replace form contents from template + notification.form_ref.form = form_templates[notification.form_ref.form_template.name]; + if(notification.form_ref.form_template.yes_text.isProvided()) + { + replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text); + } + if(notification.form_ref.form_template.no_text.isProvided()) + { + replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text); + } + if(notification.form_ref.form_template.cancel_text.isProvided()) + { + replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text); + } + if(notification.form_ref.form_template.help_text.isProvided()) + { + replaceFormText(notification.form_ref.form, "$helptext", notification.form_ref.form_template.help_text); + } + if(notification.form_ref.form_template.ignore_text.isProvided()) + { + replaceFormText(notification.form_ref.form, "$ignoretext", notification.form_ref.form_template.ignore_text); + } + } + mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification)); + } + + LL_INFOS("Notifications") << "...done" << LL_ENDL; + + return true; +} + +bool LLNotifications::loadVisibilityRules() +{ + const std::string xml_filename = "notification_visibility.xml"; + // Note that here we're looking for the "en" version, the default + // language, rather than the most localized version of this file. + std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename); + + LLNotificationVisibilityRule::Rules params; + LLSimpleXUIParser parser; + parser.readXUI(full_filename, params); + + if(!params.validateBlock()) + { + LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile")); + LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL; + return false; + } + + mVisibilityRules.clear(); + + for (const LLNotificationVisibilityRule::Rule& rule : params.rules) + { + mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule))); + } + + return true; +} + +// Add a simple notification (from XUI) +void LLNotifications::addFromCallback(const LLSD& name) +{ + add(name.asString(), LLSD(), LLSD()); +} + +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload) +{ + LLNotification::Params::Functor functor_p; + functor_p.name = name; + return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); +} + +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, const std::string& functor_name) +{ + LLNotification::Params::Functor functor_p; + functor_p.name = functor_name; + return add(LLNotification::Params().name(name) + .substitutions(substitutions) + .payload(payload) + .functor(functor_p)); +} + +//virtual +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor) +{ + LLNotification::Params::Functor functor_p; + functor_p.function = functor; + return add(LLNotification::Params().name(name) + .substitutions(substitutions) + .payload(payload) + .functor(functor_p)); +} + +// generalized add function that takes a parameter block object for more complex instantiations +LLNotificationPtr LLNotifications::add(const LLNotification::Params& p) +{ + LLNotificationPtr pNotif(new LLNotification(p)); + add(pNotif); + return pNotif; +} + + +void LLNotifications::add(const LLNotificationPtr pNotif) +{ + if (pNotif == NULL) return; + + // first see if we already have it -- if so, that's a problem + LLNotificationSet::iterator it=mItems.find(pNotif); + if (it != mItems.end()) + { + LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL; + } + + updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif); +} + +void LLNotifications::load(const LLNotificationPtr pNotif) +{ + if (pNotif == NULL) return; + + // first see if we already have it -- if so, that's a problem + LLNotificationSet::iterator it=mItems.find(pNotif); + if (it != mItems.end()) + { + LL_ERRS() << "Notification loaded a second time to the master notification channel." << LL_ENDL; + } + + updateItem(LLSD().with("sigtype", "load").with("id", pNotif->id()), pNotif); +} + +void LLNotifications::cancel(LLNotificationPtr pNotif) +{ + if (pNotif == NULL || pNotif->isCancelled()) return; + + LLNotificationSet::iterator it=mItems.find(pNotif); + if (it != mItems.end()) + { + pNotif->cancel(); + updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); + } +} + +void LLNotifications::cancelByName(const std::string& name) +{ + LLMutexLock lock(&mItemsMutex); + std::vector notifs_to_cancel; + for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end(); + it != end_it; + ++it) + { + LLNotificationPtr pNotif = *it; + if (pNotif->getName() == name) + { + notifs_to_cancel.push_back(pNotif); + } + } + + for (std::vector::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end(); + it != end_it; + ++it) + { + LLNotificationPtr pNotif = *it; + pNotif->cancel(); + updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); + } +} + +void LLNotifications::cancelByOwner(const LLUUID ownerId) +{ + LLMutexLock lock(&mItemsMutex); + std::vector notifs_to_cancel; + for (LLNotificationSet::iterator it = mItems.begin(), end_it = mItems.end(); + it != end_it; + ++it) + { + LLNotificationPtr pNotif = *it; + if (pNotif && pNotif->getPayload().get("owner_id").asUUID() == ownerId) + { + notifs_to_cancel.push_back(pNotif); + } + } + + for (std::vector::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end(); + it != end_it; + ++it) + { + LLNotificationPtr pNotif = *it; + pNotif->cancel(); + updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); + } +} + +void LLNotifications::update(const LLNotificationPtr pNotif) +{ + LLNotificationSet::iterator it=mItems.find(pNotif); + if (it != mItems.end()) + { + updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif); + } +} + + +LLNotificationPtr LLNotifications::find(LLUUID uuid) +{ + LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid))); + LLNotificationSet::iterator it=mItems.find(target); + if (it == mItems.end()) + { + LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL; + return LLNotificationPtr((LLNotification*)NULL); + } + else + { + return *it; + } +} + +std::string LLNotifications::getGlobalString(const std::string& key) const +{ + GlobalStringMap::const_iterator it = mGlobalStrings.find(key); + if (it != mGlobalStrings.end()) + { + return it->second; + } + else + { + // if we don't have the key as a global, return the key itself so that the error + // is self-diagnosing. + return key; + } +} + +void LLNotifications::setIgnoreAllNotifications(bool setting) +{ + mIgnoreAllNotifications = setting; +} +bool LLNotifications::getIgnoreAllNotifications() +{ + return mIgnoreAllNotifications; +} + +void LLNotifications::setIgnored(const std::string& name, bool ignored) +{ + LLNotificationTemplatePtr templatep = getTemplate(name); + templatep->mForm->setIgnored(ignored); +} + +bool LLNotifications::getIgnored(const std::string& name) +{ + LLNotificationTemplatePtr templatep = getTemplate(name); + return (mIgnoreAllNotifications) || ( (templatep->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) && (templatep->mForm->getIgnored()) ); +} + +bool LLNotifications::isVisibleByRules(LLNotificationPtr n) +{ + if(n->isRespondedTo()) + { + // This avoids infinite recursion in the case where the filter calls respond() + return true; + } + + VisibilityRuleList::iterator it; + + for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++) + { + // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule. + LL_DEBUGS("Notifications") + << "notification \"" << n->getName() << "\" " + << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, " + << "name = \"" << (*it)->mName << "\" " + << "tag = \"" << (*it)->mTag << "\" " + << "type = \"" << (*it)->mType << "\" " + << LL_ENDL; + + if(!(*it)->mType.empty()) + { + if((*it)->mType != n->getType()) + { + // Type doesn't match, so skip this rule. + continue; + } + } + + if(!(*it)->mTag.empty()) + { + // check this notification's tag(s) against it->mTag and continue if no match is found. + if(!n->matchesTag((*it)->mTag)) + { + // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule. + continue; + } + } + + if(!(*it)->mName.empty()) + { + // check this notification's name against the notification's name and continue if no match is found. + if((*it)->mName != n->getName()) + { + // This rule's non-empty name didn't match the notification. Skip this rule. + continue; + } + } + + // If we got here, the rule matches. Don't evaluate subsequent rules. + if(!(*it)->mVisible) + { + // This notification is being hidden. + + if((*it)->mResponse.empty()) + { + // Response property is empty. Cancel this notification. + LL_DEBUGS("Notifications") << "cancelling notification " << n->getName() << LL_ENDL; + + cancel(n); + } + else + { + // Response property is not empty. Return the specified response. + LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON); + // TODO: verify that the response template has an item with the correct name + response[(*it)->mResponse] = true; + + LL_DEBUGS("Notifications") << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL; + + n->respond(response); + } + + return false; + } + + // If we got here, exit the loop and return true. + break; + } + + LL_DEBUGS("Notifications") << "allowing notification " << n->getName() << LL_ENDL; + + return true; +} + + +// --- +// END OF LLNotifications implementation +// ========================================================= + +std::ostream& operator<<(std::ostream& s, const LLNotification& notification) +{ + s << notification.summarize(); + return s; +} + +void LLPostponedNotification::lookupName(const LLUUID& id, + bool is_group) +{ + if (is_group) + { + gCacheName->getGroup(id, + boost::bind(&LLPostponedNotification::onGroupNameCache, + this, _1, _2, _3)); + } + else + { + fetchAvatarName(id); + } +} + +void LLPostponedNotification::onGroupNameCache(const LLUUID& id, + const std::string& full_name, + bool is_group) +{ + finalizeName(full_name); +} + +void LLPostponedNotification::fetchAvatarName(const LLUUID& id) +{ + if (id.notNull()) + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + + mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLPostponedNotification::onAvatarNameCache, this, _1, _2)); + } +} + +void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + mAvatarNameCacheConnection.disconnect(); + + std::string name = av_name.getCompleteName(); + + // from PE merge - we should figure out if this is the right thing to do + if (name.empty()) + { + LL_WARNS("Notifications") << "Empty name received for Id: " << agent_id << LL_ENDL; + name = SYSTEM_FROM; + } + + finalizeName(name); +} + +void LLPostponedNotification::finalizeName(const std::string& name) +{ + mName = name; + modifyNotificationParams(); + LLNotifications::instance().add(mParams); + cleanup(); +} diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index c3796252c7..e7159de94c 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -1,1133 +1,1133 @@ -/** -* @file llnotifications.h -* @brief Non-UI manager and support for keeping a prioritized list of notifications -* @author Q (with assistance from Richard and Coco) -* -* $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_LLNOTIFICATIONS_H -#define LL_LLNOTIFICATIONS_H - -/** - * This system is intended to provide a singleton mechanism for adding - * notifications to one of an arbitrary set of event channels. - * - * Controlling JIRA: DEV-9061 - * - * Every notification has (see code for full list): - * - a textual name, which is used to look up its template in the XML files - * - a payload, which is a block of LLSD - * - a channel, which is normally extracted from the XML files but - * can be overridden. - * - a timestamp, used to order the notifications - * - expiration time -- if nonzero, specifies a time after which the - * notification will no longer be valid. - * - a callback name and a couple of status bits related to callbacks (see below) - * - * There is a management class called LLNotifications, which is an LLSingleton. - * The class maintains a collection of all of the notifications received - * or processed during this session, and also manages the persistence - * of those notifications that must be persisted. - * - * We also have Channels. A channel is a view on a collection of notifications; - * The collection is defined by a filter function that controls which - * notifications are in the channel, and its ordering is controlled by - * a comparator. - * - * There is a hierarchy of channels; notifications flow down from - * the management class (LLNotifications, which itself inherits from - * The channel base class) to the individual channels. - * Any change to notifications (add, delete, modify) is - * automatically propagated through the channel hierarchy. - * - * We provide methods for adding a new notification, for removing - * one, and for managing channels. Channels are relatively cheap to construct - * and maintain, so in general, human interfaces should use channels to - * select and manage their lists of notifications. - * - * We also maintain a collection of templates that are loaded from the - * XML file of template translations. The system supports substitution - * of named variables from the payload into the XML file. - * - * By default, only the "unknown message" template is built into the system. - * It is not an error to add a notification that's not found in the - * template system, but it is logged. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "llevents.h" -#include "llfunctorregistry.h" -#include "llinitparam.h" -#include "llinstancetracker.h" -#include "llmortician.h" -#include "llnotificationptr.h" -#include "llpointer.h" -#include "llrefcount.h" -#include "llsdparam.h" - -#include "llnotificationslistener.h" - -class LLAvatarName; -typedef enum e_notification_priority -{ - NOTIFICATION_PRIORITY_UNSPECIFIED, - NOTIFICATION_PRIORITY_LOW, - NOTIFICATION_PRIORITY_NORMAL, - NOTIFICATION_PRIORITY_HIGH, - NOTIFICATION_PRIORITY_CRITICAL -} ENotificationPriority; - -struct NotificationPriorityValues : public LLInitParam::TypeValuesHelper -{ - static void declareValues(); -}; - -class LLNotificationResponderInterface -{ -public: - LLNotificationResponderInterface(){}; - virtual ~LLNotificationResponderInterface(){}; - - virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0; - - virtual LLSD asLLSD() = 0; - - virtual void fromLLSD(const LLSD& params) = 0; -}; - -typedef boost::function LLNotificationResponder; - -typedef std::shared_ptr LLNotificationResponderPtr; - -typedef LLFunctorRegistry LLNotificationFunctorRegistry; -typedef LLFunctorRegistration LLNotificationFunctorRegistration; - -// context data that can be looked up via a notification's payload by the display logic -// derive from this class to implement specific contexts -class LLNotificationContext : public LLInstanceTracker -{ -public: - - LLNotificationContext() : LLInstanceTracker(LLUUID::generateNewID()) - { - } - - virtual ~LLNotificationContext() {} - - LLSD asLLSD() const - { - return getKey(); - } - -private: - -}; - -// Contains notification form data, such as buttons and text fields along with -// manipulator functions -class LLNotificationForm -{ - LOG_CLASS(LLNotificationForm); - -public: - struct FormElementBase : public LLInitParam::Block - { - Optional name; - Optional enabled; - - FormElementBase(); - }; - - struct FormIgnore : public LLInitParam::Block - { - Optional text; - Optional save_option; - Optional control; - Optional invert_control; - Optional session_only; - Optional checkbox_only; - - FormIgnore(); - }; - - struct FormButton : public LLInitParam::Block - { - Mandatory index; - Mandatory text; - Optional ignore; - Optional is_default; - Optional width; - - Mandatory type; - - FormButton(); - }; - - struct FormInput : public LLInitParam::Block - { - Mandatory type; - Optional width; - Optional max_length_chars; - Optional allow_emoji; - Optional text; - - Optional value; - FormInput(); - }; - - struct FormElement : public LLInitParam::ChoiceBlock - { - Alternative button; - Alternative input; - - FormElement(); - }; - - struct FormElements : public LLInitParam::Block - { - Multiple elements; - FormElements(); - }; - - struct Params : public LLInitParam::Block - { - Optional name; - Optional ignore; - Optional form_elements; - - Params(); - }; - - typedef enum e_ignore_type - { - IGNORE_CHECKBOX_ONLY = -1, // ignore won't be handled, will set value/checkbox only - IGNORE_NO = 0, - IGNORE_WITH_DEFAULT_RESPONSE, - IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY, - IGNORE_WITH_LAST_RESPONSE, - IGNORE_SHOW_AGAIN - } EIgnoreType; - - LLNotificationForm(); - LLNotificationForm(const LLNotificationForm&); - LLNotificationForm(const LLSD& sd); - LLNotificationForm(const std::string& name, const Params& p); - - void fromLLSD(const LLSD& sd); - LLSD asLLSD() const; - - S32 getNumElements() { return mFormData.size(); } - LLSD getElement(S32 index) { return mFormData.get(index); } - LLSD getElement(const std::string& element_name); - void getElements(LLSD& elements, S32 offset = 0); - bool hasElement(const std::string& element_name) const; - bool getElementEnabled(const std::string& element_name) const; - void setElementEnabled(const std::string& element_name, bool enabled); - void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD(), bool enabled = true); - void formatElements(const LLSD& substitutions); - // appends form elements from another form serialized as LLSD - void append(const LLSD& sub_form); - std::string getDefaultOption(); - LLPointer getIgnoreSetting(); - bool getIgnored(); - void setIgnored(bool ignored); - - EIgnoreType getIgnoreType() { return mIgnore; } - std::string getIgnoreMessage() { return mIgnoreMsg; } - -private: - LLSD mFormData; - EIgnoreType mIgnore; - std::string mIgnoreMsg; - LLPointer mIgnoreSetting; - bool mInvertSetting; -}; - -typedef std::shared_ptr LLNotificationFormPtr; - - -struct LLNotificationTemplate; - -// we want to keep a map of these by name, and it's best to manage them -// with smart pointers -typedef std::shared_ptr LLNotificationTemplatePtr; - - -struct LLNotificationVisibilityRule; - -typedef std::shared_ptr LLNotificationVisibilityRulePtr; - -/** - * @class LLNotification - * @brief The object that expresses the details of a notification - * - * We make this noncopyable because - * we want to manage these through LLNotificationPtr, and only - * ever create one instance of any given notification. - * - * The enable_shared_from_this flag ensures that if we construct - * a smart pointer from a notification, we'll always get the same - * shared pointer. - */ -class LLNotification : - boost::noncopyable, - public std::enable_shared_from_this -{ -LOG_CLASS(LLNotification); -friend class LLNotifications; - -public: - - // parameter object used to instantiate a new notification - struct Params : public LLInitParam::Block - { - friend class LLNotification; - - Mandatory name; - Optional id; - Optional substitutions, - form_elements, - payload; - Optional priority; - Optional time_stamp, - expiry; - Optional context; - Optional responder; - Optional offer_from_agent; - Optional is_dnd; - - struct Functor : public LLInitParam::ChoiceBlock - { - Alternative name; - Alternative function; - Alternative responder; - Alternative responder_sd; - - Functor() - : name("responseFunctor"), - function("functor"), - responder("responder"), - responder_sd("responder_sd") - {} - }; - Optional functor; - - Params() - : name("name"), - id("id"), - priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), - time_stamp("time"), - payload("payload"), - form_elements("form"), - substitutions("substitutions"), - expiry("expiry"), - offer_from_agent("offer_from_agent", false), - is_dnd("is_dnd", false) - { - time_stamp = LLDate::now(); - responder = NULL; - } - - Params(const std::string& _name) - : name("name"), - priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), - time_stamp("time"), - payload("payload"), - form_elements("form"), - substitutions("substitutions"), - expiry("expiry"), - offer_from_agent("offer_from_agent", false), - is_dnd("is_dnd", false) - { - functor.name = _name; - name = _name; - time_stamp = LLDate::now(); - responder = NULL; - } - }; - - LLNotificationResponderPtr getResponderPtr() { return mResponder; } - -private: - - const LLUUID mId; - LLSD mPayload; - LLSD mSubstitutions; - LLDate mTimestamp; - LLDate mExpiresAt; - bool mCancelled; - bool mRespondedTo; // once the notification has been responded to, this becomes true - LLSD mResponse; - bool mIgnored; - ENotificationPriority mPriority; - LLNotificationFormPtr mForm; - void* mResponderObj; // TODO - refactor/remove this field - LLNotificationResponderPtr mResponder; - bool mOfferFromAgent; - bool mIsDND; - - // a reference to the template - LLNotificationTemplatePtr mTemplatep; - - /* - We want to be able to store and reload notifications so that they can survive - a shutdown/restart of the client. So we can't simply pass in callbacks; - we have to specify a callback mechanism that can be used by name rather than - by some arbitrary pointer -- and then people have to initialize callbacks - in some useful location. So we use LLNotificationFunctorRegistry to manage them. - */ - std::string mResponseFunctorName; - - /* - In cases where we want to specify an explict, non-persisted callback, - we store that in the callback registry under a dynamically generated - key, and store the key in the notification, so we can still look it up - using the same mechanism. - */ - bool mTemporaryResponder; - - // keep track of other notifications combined with COMBINE_WITH_NEW - std::vector mCombinedNotifications; - - void init(const std::string& template_name, const LLSD& form_elements); - - void cancel(); - -public: - LLNotification(const LLSDParamAdapter& p); - - void setResponseFunctor(std::string const &responseFunctorName); - - void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb); - - void setResponseFunctor(const LLNotificationResponderPtr& responder); - - typedef enum e_response_template_type - { - WITHOUT_DEFAULT_BUTTON, - WITH_DEFAULT_BUTTON - } EResponseTemplateType; - - // return response LLSD filled in with default form contents and (optionally) the default button selected - LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON); - - // returns index of first button with value==true - // usually this the button the user clicked on - // returns -1 if no button clicked (e.g. form has not been displayed) - static S32 getSelectedOption(const LLSD& notification, const LLSD& response); - // returns name of first button with value==true - static std::string getSelectedOptionName(const LLSD& notification); - - // after someone responds to a notification (usually by clicking a button, - // but sometimes by filling out a little form and THEN clicking a button), - // the result of the response (the name and value of the button clicked, - // plus any other data) should be packaged up as LLSD, then passed as a - // parameter to the notification's respond() method here. This will look up - // and call the appropriate responder. - // - // response is notification serialized as LLSD: - // ["name"] = notification name - // ["form"] = LLSD tree that includes form description and any prefilled form data - // ["response"] = form data filled in by user - // (including, but not limited to which button they clicked on) - // ["payload"] = transaction specific data, such as ["source_id"] (originator of notification), - // ["item_id"] (attached inventory item), etc. - // ["substitutions"] = string substitutions used to generate notification message - // from the template - // ["time"] = time at which notification was generated; - // ["expiry"] = time at which notification expires; - // ["responseFunctor"] = name of registered functor that handles responses to notification; - LLSD asLLSD(bool excludeTemplateElements = false); - - const LLNotificationFormPtr getForm(); - void updateForm(const LLNotificationFormPtr& form); - - void repost(); - - void respond(const LLSD& sd); - void respondWithDefault(); - - void* getResponder() { return mResponderObj; } - - void setResponder(void* responder) { mResponderObj = responder; } - - void setIgnored(bool ignore); - - bool isCancelled() const - { - return mCancelled; - } - - bool isRespondedTo() const - { - return mRespondedTo; - } - - bool isActive() const - { - return !isRespondedTo() - && !isCancelled() - && !isExpired(); - } - - const LLSD& getResponse() { return mResponse; } - - bool isIgnored() const - { - return mIgnored; - } - - const std::string& getName() const; - - const std::string& getIcon() const; - - bool isPersistent() const; - - const LLUUID& id() const - { - return mId; - } - - const LLSD& getPayload() const - { - return mPayload; - } - - const LLSD& getSubstitutions() const - { - return mSubstitutions; - } - - const LLDate& getDate() const - { - return mTimestamp; - } - - bool getOfferFromAgent() const - { - return mOfferFromAgent; - } - - bool isDND() const - { - return mIsDND; - } - - void setDND(const bool flag) - { - mIsDND = flag; - } - - std::string getType() const; - std::string getMessage() const; - std::string getFooter() const; - std::string getLabel() const; - std::string getURL() const; - S32 getURLOption() const; - S32 getURLOpenExternally() const; //for url responce option - bool getForceUrlsExternal() const; - bool canLogToChat() const; - bool canLogToIM() const; - bool canShowToast() const; - bool canFadeToast() const; - bool hasFormElements() const; - void playSound(); - - typedef enum e_combine_behavior - { - REPLACE_WITH_NEW, - COMBINE_WITH_NEW, - KEEP_OLD, - CANCEL_OLD - - } ECombineBehavior; - - ECombineBehavior getCombineBehavior() const; - - const LLDate getExpiration() const - { - return mExpiresAt; - } - - ENotificationPriority getPriority() const - { - return mPriority; - } - - const LLUUID getID() const - { - return mId; - } - - // comparing two notifications normally means comparing them by UUID (so we can look them - // up quickly this way) - bool operator<(const LLNotification& rhs) const - { - return mId < rhs.mId; - } - - bool operator==(const LLNotification& rhs) const - { - return mId == rhs.mId; - } - - bool operator!=(const LLNotification& rhs) const - { - return !operator==(rhs); - } - - bool isSameObjectAs(const LLNotification* rhs) const - { - return this == rhs; - } - - // this object has been updated, so tell all our clients - void update(); - - void updateFrom(LLNotificationPtr other); - - // A fuzzy equals comparator. - // true only if both notifications have the same template and - // 1) flagged as unique (there can be only one of these) OR - // 2) all required payload fields of each also exist in the other. - bool isEquivalentTo(LLNotificationPtr that) const; - - // if the current time is greater than the expiration, the notification is expired - bool isExpired() const - { - if (mExpiresAt.secondsSinceEpoch() == 0) - { - return false; - } - - LLDate rightnow = LLDate::now(); - return rightnow > mExpiresAt; - } - - std::string summarize() const; - - bool hasUniquenessConstraints() const; - - bool matchesTag(const std::string& tag); - - virtual ~LLNotification() {} -}; - -std::ostream& operator<<(std::ostream& s, const LLNotification& notification); - -namespace LLNotificationFilters -{ - // a sample filter - bool includeEverything(LLNotificationPtr p); - - typedef enum e_comparison - { - EQUAL, - LESS, - GREATER, - LESS_EQUAL, - GREATER_EQUAL - } EComparison; - - // generic filter functor that takes method or member variable reference - template - struct filterBy - { - typedef boost::function field_t; - typedef typename boost::remove_reference::type value_t; - - filterBy(field_t field, value_t value, EComparison comparison = EQUAL) - : mField(field), - mFilterValue(value), - mComparison(comparison) - { - } - - bool operator()(LLNotificationPtr p) - { - switch(mComparison) - { - case EQUAL: - return mField(p) == mFilterValue; - case LESS: - return mField(p) < mFilterValue; - case GREATER: - return mField(p) > mFilterValue; - case LESS_EQUAL: - return mField(p) <= mFilterValue; - case GREATER_EQUAL: - return mField(p) >= mFilterValue; - default: - return false; - } - } - - field_t mField; - value_t mFilterValue; - EComparison mComparison; - }; -}; - -namespace LLNotificationComparators -{ - struct orderByUUID - { - bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) const - { - return lhs->id() < rhs->id(); - } - }; -}; - -typedef boost::function LLNotificationFilter; -typedef std::set LLNotificationSet; -typedef std::multimap LLNotificationMap; - -// ======================================================== -// Abstract base class (interface) for a channel; also used for the master container. -// This lets us arrange channels into a call hierarchy. - -// We maintain a hierarchy of notification channels; events are always started at the top -// and propagated through the hierarchy only if they pass a filter. -// Any channel can be created with a parent. A null parent (empty string) means it's -// tied to the root of the tree (the LLNotifications class itself). -// The default hierarchy looks like this: -// -// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History -// +-- Alerts -// +-- Notifications -// -// In general, new channels that want to only see notifications that pass through -// all of the built-in tests should attach to the "Visible" channel -// -class LLNotificationChannelBase : - public LLEventTrackable, - public LLRefCount -{ - LOG_CLASS(LLNotificationChannelBase); -public: - LLNotificationChannelBase(LLNotificationFilter filter) - : mFilter(filter) - , mItems() - , mItemsMutex() - {} - - virtual ~LLNotificationChannelBase() - { - // explicit cleanup for easier issue detection - mChanged.disconnect_all_slots(); - mPassedFilter.disconnect_all_slots(); - mFailedFilter.disconnect_all_slots(); - LLMutexLock lock(&mItemsMutex); - mItems.clear(); - } - // you can also connect to a Channel, so you can be notified of - // changes to this channel - LLBoundListener connectChanged(const LLEventListener& slot) - { - // Call this->connectChangedImpl() to actually connect it. - return connectChangedImpl(slot); - } - LLBoundListener connectAtFrontChanged(const LLEventListener& slot) - { - return connectAtFrontChangedImpl(slot); - } - LLBoundListener connectPassedFilter(const LLEventListener& slot) - { - // see comments in connectChanged() - return connectPassedFilterImpl(slot); - } - LLBoundListener connectFailedFilter(const LLEventListener& slot) - { - // see comments in connectChanged() - return connectFailedFilterImpl(slot); - } - - // use this when items change or to add a new one - bool updateItem(const LLSD& payload); - const LLNotificationFilter& getFilter() { return mFilter; } - -protected: - LLBoundListener connectChangedImpl(const LLEventListener& slot); - LLBoundListener connectAtFrontChangedImpl(const LLEventListener& slot); - LLBoundListener connectPassedFilterImpl(const LLEventListener& slot); - LLBoundListener connectFailedFilterImpl(const LLEventListener& slot); - - LLNotificationSet mItems; - LLStandardSignal mChanged; - LLStandardSignal mPassedFilter; - LLStandardSignal mFailedFilter; - LLMutex mItemsMutex; - - // these are action methods that subclasses can override to take action - // on specific types of changes; the management of the mItems list is - // still handled by the generic handler. - virtual void onLoad(LLNotificationPtr p) {} - virtual void onAdd(LLNotificationPtr p) {} - virtual void onDelete(LLNotificationPtr p) {} - virtual void onChange(LLNotificationPtr p) {} - - virtual void onFilterPass(LLNotificationPtr p) {} - virtual void onFilterFail(LLNotificationPtr p) {} - - bool updateItem(const LLSD& payload, LLNotificationPtr pNotification); - LLNotificationFilter mFilter; -}; - -// The type of the pointers that we're going to manage in the NotificationQueue system -// Because LLNotifications is a singleton, we don't actually expect to ever -// destroy it, but if it becomes necessary to do so, the shared_ptr model -// will ensure that we don't leak resources. -class LLNotificationChannel; -typedef boost::intrusive_ptr LLNotificationChannelPtr; - -// manages a list of notifications -// Note that if this is ever copied around, we might find ourselves with multiple copies -// of a queue with notifications being added to different nonequivalent copies. So we -// make it inherit from boost::noncopyable, and then create a map of LLPointer to manage it. -// -class LLNotificationChannel : - boost::noncopyable, - public LLNotificationChannelBase, - public LLInstanceTracker -{ - LOG_CLASS(LLNotificationChannel); - -public: - // Notification Channels have a filter, which determines which notifications - // will be added to this channel. - // Channel filters cannot change. - struct Params : public LLInitParam::Block - { - Mandatory name; - Optional filter; - Multiple sources; - }; - - LLNotificationChannel(const Params& p = Params()); - LLNotificationChannel(const std::string& name, const std::string& parent, LLNotificationFilter filter); - - virtual ~LLNotificationChannel(); - typedef LLNotificationSet::iterator Iterator; - - std::string getName() const { return mName; } - typedef std::vector::const_iterator parents_iter; - boost::iterator_range getParents() const - { - return boost::iterator_range(mParents); - } - - bool isEmpty() const; - S32 size() const; - size_t size(); - - typedef boost::function NotificationProcess; - void forEachNotification(NotificationProcess process); - - std::string summarize(); - -protected: - void connectToChannel(const std::string& channel_name); - -private: - std::string mName; - std::vector mParents; - std::vector mListeners; -}; - -// An interface class to provide a clean linker seam to the LLNotifications class. -// Extend this interface as needed for your use of LLNotifications. -class LLNotificationsInterface -{ -public: - virtual LLNotificationPtr add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - LLNotificationFunctorRegistry::ResponseFunctor functor) = 0; -}; - -class LLNotifications : - public LLNotificationsInterface, - public LLSingleton, - public LLNotificationChannelBase -{ - LLSINGLETON(LLNotifications); - LOG_CLASS(LLNotifications); - virtual ~LLNotifications() {} - -public: - - // Needed to clear up RefCounted things prior to actual destruction - // as the singleton nature of the class makes them do "bad things" - // on at least Mac, if not all 3 platforms - // - void clear(); - - // load all notification descriptions from file - // calling more than once will overwrite existing templates - // but never delete a template - bool loadTemplates(); - - // load visibility rules from file; - // OK to call more than once because it will reload - bool loadVisibilityRules(); - - // Add a simple notification (from XUI) - void addFromCallback(const LLSD& name); - - // *NOTE: To add simple notifications, #include "llnotificationsutil.h" - // and use LLNotificationsUtil::add("MyNote") or add("MyNote", args) - LLNotificationPtr add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload); - LLNotificationPtr add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - const std::string& functor_name); - /* virtual */ LLNotificationPtr add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - LLNotificationFunctorRegistry::ResponseFunctor functor) override; - LLNotificationPtr add(const LLNotification::Params& p); - - void add(const LLNotificationPtr pNotif); - void load(const LLNotificationPtr pNotif); - void cancel(LLNotificationPtr pNotif); - void cancelByName(const std::string& name); - void cancelByOwner(const LLUUID ownerId); - void update(const LLNotificationPtr pNotif); - - LLNotificationPtr find(LLUUID uuid); - - // This is all stuff for managing the templates - // take your template out - LLNotificationTemplatePtr getTemplate(const std::string& name); - - // get the whole collection - typedef std::vector TemplateNames; - TemplateNames getTemplateNames() const; // returns a list of notification names - - typedef std::map TemplateMap; - - TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); } - TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); } - - // test for existence - bool templateExists(const std::string& name); - - typedef std::list VisibilityRuleList; - - void forceResponse(const LLNotification::Params& params, S32 option); - - void createDefaultChannels(); - - LLNotificationChannelPtr getChannel(const std::string& channelName); - - std::string getGlobalString(const std::string& key) const; - - void setIgnoreAllNotifications(bool ignore); - bool getIgnoreAllNotifications(); - - void setIgnored(const std::string& name, bool ignored); - bool getIgnored(const std::string& name); - - bool isVisibleByRules(LLNotificationPtr pNotification); - -private: - /*virtual*/ void initSingleton() override; - /*virtual*/ void cleanupSingleton() override; - - void loadPersistentNotifications(); - - bool expirationFilter(LLNotificationPtr pNotification); - bool expirationHandler(const LLSD& payload); - bool uniqueFilter(LLNotificationPtr pNotification); - bool uniqueHandler(const LLSD& payload); - bool failedUniquenessTest(const LLSD& payload); - LLNotificationChannelPtr pHistoryChannel; - LLNotificationChannelPtr pExpirationChannel; - - TemplateMap mTemplates; - - VisibilityRuleList mVisibilityRules; - - std::string mFileName; - - LLNotificationMap mUniqueNotifications; - - typedef std::map GlobalStringMap; - GlobalStringMap mGlobalStrings; - - bool mIgnoreAllNotifications; - - std::unique_ptr mListener; - - std::vector mDefaultChannels; -}; - -/** - * Abstract class for postponed notifications. - * Provides possibility to add notification after specified by id avatar or group will be - * received from cache name. The object of this type automatically well be deleted - * by cleanup method after respond will be received from cache name. - * - * To add custom postponed notification to the notification system client should: - * 1 create class derived from LLPostponedNotification; - * 2 call LLPostponedNotification::add method; - */ -class LLPostponedNotification : public LLMortician -{ -public: - /** - * Performs hooking cache name callback which will add notification to notifications system. - * Type of added notification should be specified by template parameter T - * and non-private derived from LLPostponedNotification class, - * otherwise compilation error will occur. - */ - template - static void add(const LLNotification::Params& params, - const LLUUID& id, bool is_group) - { - // upcast T to the base type to restrict T derivation from LLPostponedNotification - LLPostponedNotification* thiz = new T(); - thiz->mParams = params; - - // Avoid header file dependency on llcachename.h - thiz->lookupName(id, is_group); - } - -private: - void lookupName(const LLUUID& id, bool is_group); - // only used for groups - void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group); - // only used for avatars - void fetchAvatarName(const LLUUID& id); - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); - // used for both group and avatar names - void finalizeName(const std::string& name); - - void cleanup() - { - die(); - } - -protected: - LLPostponedNotification() - : mParams(), - mName(), - mAvatarNameCacheConnection() - {} - - virtual ~LLPostponedNotification() - { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - } - - /** - * Abstract method provides possibility to modify notification parameters and - * will be called after cache name retrieve information about avatar or group - * and before notification will be added to the notification system. - */ - virtual void modifyNotificationParams() = 0; - - LLNotification::Params mParams; - std::string mName; - boost::signals2::connection mAvatarNameCacheConnection; -}; - -// Stores only persistent notifications. -// Class users can use connectChanged() to process persistent notifications -// (see LLPersistentNotificationStorage for example). -class LLPersistentNotificationChannel : public LLNotificationChannel -{ - LOG_CLASS(LLPersistentNotificationChannel); -public: - LLPersistentNotificationChannel() - : LLNotificationChannel("Persistent", "Visible", ¬ificationFilter) - {} - - virtual ~LLPersistentNotificationChannel() - { - mHistory.clear(); - } - - typedef std::vector history_list_t; - history_list_t::iterator beginHistory() { sortHistory(); return mHistory.begin(); } - history_list_t::iterator endHistory() { return mHistory.end(); } - -private: - struct sortByTime - { - S32 operator ()(const LLNotificationPtr& a, const LLNotificationPtr& b) - { - return a->getDate() < b->getDate(); - } - }; - - void sortHistory() - { - std::sort(mHistory.begin(), mHistory.end(), sortByTime()); - } - - // The channel gets all persistent notifications except those that have been canceled - static bool notificationFilter(LLNotificationPtr pNotification) - { - bool handle_notification = false; - - handle_notification = pNotification->isPersistent() - && !pNotification->isCancelled(); - - return handle_notification; - } - - void onAdd(LLNotificationPtr p) - { - mHistory.push_back(p); - } - - void onLoad(LLNotificationPtr p) - { - mHistory.push_back(p); - } - - std::vector mHistory; -}; - -#endif//LL_LLNOTIFICATIONS_H - +/** +* @file llnotifications.h +* @brief Non-UI manager and support for keeping a prioritized list of notifications +* @author Q (with assistance from Richard and Coco) +* +* $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_LLNOTIFICATIONS_H +#define LL_LLNOTIFICATIONS_H + +/** + * This system is intended to provide a singleton mechanism for adding + * notifications to one of an arbitrary set of event channels. + * + * Controlling JIRA: DEV-9061 + * + * Every notification has (see code for full list): + * - a textual name, which is used to look up its template in the XML files + * - a payload, which is a block of LLSD + * - a channel, which is normally extracted from the XML files but + * can be overridden. + * - a timestamp, used to order the notifications + * - expiration time -- if nonzero, specifies a time after which the + * notification will no longer be valid. + * - a callback name and a couple of status bits related to callbacks (see below) + * + * There is a management class called LLNotifications, which is an LLSingleton. + * The class maintains a collection of all of the notifications received + * or processed during this session, and also manages the persistence + * of those notifications that must be persisted. + * + * We also have Channels. A channel is a view on a collection of notifications; + * The collection is defined by a filter function that controls which + * notifications are in the channel, and its ordering is controlled by + * a comparator. + * + * There is a hierarchy of channels; notifications flow down from + * the management class (LLNotifications, which itself inherits from + * The channel base class) to the individual channels. + * Any change to notifications (add, delete, modify) is + * automatically propagated through the channel hierarchy. + * + * We provide methods for adding a new notification, for removing + * one, and for managing channels. Channels are relatively cheap to construct + * and maintain, so in general, human interfaces should use channels to + * select and manage their lists of notifications. + * + * We also maintain a collection of templates that are loaded from the + * XML file of template translations. The system supports substitution + * of named variables from the payload into the XML file. + * + * By default, only the "unknown message" template is built into the system. + * It is not an error to add a notification that's not found in the + * template system, but it is logged. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "llevents.h" +#include "llfunctorregistry.h" +#include "llinitparam.h" +#include "llinstancetracker.h" +#include "llmortician.h" +#include "llnotificationptr.h" +#include "llpointer.h" +#include "llrefcount.h" +#include "llsdparam.h" + +#include "llnotificationslistener.h" + +class LLAvatarName; +typedef enum e_notification_priority +{ + NOTIFICATION_PRIORITY_UNSPECIFIED, + NOTIFICATION_PRIORITY_LOW, + NOTIFICATION_PRIORITY_NORMAL, + NOTIFICATION_PRIORITY_HIGH, + NOTIFICATION_PRIORITY_CRITICAL +} ENotificationPriority; + +struct NotificationPriorityValues : public LLInitParam::TypeValuesHelper +{ + static void declareValues(); +}; + +class LLNotificationResponderInterface +{ +public: + LLNotificationResponderInterface(){}; + virtual ~LLNotificationResponderInterface(){}; + + virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0; + + virtual LLSD asLLSD() = 0; + + virtual void fromLLSD(const LLSD& params) = 0; +}; + +typedef boost::function LLNotificationResponder; + +typedef std::shared_ptr LLNotificationResponderPtr; + +typedef LLFunctorRegistry LLNotificationFunctorRegistry; +typedef LLFunctorRegistration LLNotificationFunctorRegistration; + +// context data that can be looked up via a notification's payload by the display logic +// derive from this class to implement specific contexts +class LLNotificationContext : public LLInstanceTracker +{ +public: + + LLNotificationContext() : LLInstanceTracker(LLUUID::generateNewID()) + { + } + + virtual ~LLNotificationContext() {} + + LLSD asLLSD() const + { + return getKey(); + } + +private: + +}; + +// Contains notification form data, such as buttons and text fields along with +// manipulator functions +class LLNotificationForm +{ + LOG_CLASS(LLNotificationForm); + +public: + struct FormElementBase : public LLInitParam::Block + { + Optional name; + Optional enabled; + + FormElementBase(); + }; + + struct FormIgnore : public LLInitParam::Block + { + Optional text; + Optional save_option; + Optional control; + Optional invert_control; + Optional session_only; + Optional checkbox_only; + + FormIgnore(); + }; + + struct FormButton : public LLInitParam::Block + { + Mandatory index; + Mandatory text; + Optional ignore; + Optional is_default; + Optional width; + + Mandatory type; + + FormButton(); + }; + + struct FormInput : public LLInitParam::Block + { + Mandatory type; + Optional width; + Optional max_length_chars; + Optional allow_emoji; + Optional text; + + Optional value; + FormInput(); + }; + + struct FormElement : public LLInitParam::ChoiceBlock + { + Alternative button; + Alternative input; + + FormElement(); + }; + + struct FormElements : public LLInitParam::Block + { + Multiple elements; + FormElements(); + }; + + struct Params : public LLInitParam::Block + { + Optional name; + Optional ignore; + Optional form_elements; + + Params(); + }; + + typedef enum e_ignore_type + { + IGNORE_CHECKBOX_ONLY = -1, // ignore won't be handled, will set value/checkbox only + IGNORE_NO = 0, + IGNORE_WITH_DEFAULT_RESPONSE, + IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY, + IGNORE_WITH_LAST_RESPONSE, + IGNORE_SHOW_AGAIN + } EIgnoreType; + + LLNotificationForm(); + LLNotificationForm(const LLNotificationForm&); + LLNotificationForm(const LLSD& sd); + LLNotificationForm(const std::string& name, const Params& p); + + void fromLLSD(const LLSD& sd); + LLSD asLLSD() const; + + S32 getNumElements() { return mFormData.size(); } + LLSD getElement(S32 index) { return mFormData.get(index); } + LLSD getElement(const std::string& element_name); + void getElements(LLSD& elements, S32 offset = 0); + bool hasElement(const std::string& element_name) const; + bool getElementEnabled(const std::string& element_name) const; + void setElementEnabled(const std::string& element_name, bool enabled); + void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD(), bool enabled = true); + void formatElements(const LLSD& substitutions); + // appends form elements from another form serialized as LLSD + void append(const LLSD& sub_form); + std::string getDefaultOption(); + LLPointer getIgnoreSetting(); + bool getIgnored(); + void setIgnored(bool ignored); + + EIgnoreType getIgnoreType() { return mIgnore; } + std::string getIgnoreMessage() { return mIgnoreMsg; } + +private: + LLSD mFormData; + EIgnoreType mIgnore; + std::string mIgnoreMsg; + LLPointer mIgnoreSetting; + bool mInvertSetting; +}; + +typedef std::shared_ptr LLNotificationFormPtr; + + +struct LLNotificationTemplate; + +// we want to keep a map of these by name, and it's best to manage them +// with smart pointers +typedef std::shared_ptr LLNotificationTemplatePtr; + + +struct LLNotificationVisibilityRule; + +typedef std::shared_ptr LLNotificationVisibilityRulePtr; + +/** + * @class LLNotification + * @brief The object that expresses the details of a notification + * + * We make this noncopyable because + * we want to manage these through LLNotificationPtr, and only + * ever create one instance of any given notification. + * + * The enable_shared_from_this flag ensures that if we construct + * a smart pointer from a notification, we'll always get the same + * shared pointer. + */ +class LLNotification : + boost::noncopyable, + public std::enable_shared_from_this +{ +LOG_CLASS(LLNotification); +friend class LLNotifications; + +public: + + // parameter object used to instantiate a new notification + struct Params : public LLInitParam::Block + { + friend class LLNotification; + + Mandatory name; + Optional id; + Optional substitutions, + form_elements, + payload; + Optional priority; + Optional time_stamp, + expiry; + Optional context; + Optional responder; + Optional offer_from_agent; + Optional is_dnd; + + struct Functor : public LLInitParam::ChoiceBlock + { + Alternative name; + Alternative function; + Alternative responder; + Alternative responder_sd; + + Functor() + : name("responseFunctor"), + function("functor"), + responder("responder"), + responder_sd("responder_sd") + {} + }; + Optional functor; + + Params() + : name("name"), + id("id"), + priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), + time_stamp("time"), + payload("payload"), + form_elements("form"), + substitutions("substitutions"), + expiry("expiry"), + offer_from_agent("offer_from_agent", false), + is_dnd("is_dnd", false) + { + time_stamp = LLDate::now(); + responder = NULL; + } + + Params(const std::string& _name) + : name("name"), + priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), + time_stamp("time"), + payload("payload"), + form_elements("form"), + substitutions("substitutions"), + expiry("expiry"), + offer_from_agent("offer_from_agent", false), + is_dnd("is_dnd", false) + { + functor.name = _name; + name = _name; + time_stamp = LLDate::now(); + responder = NULL; + } + }; + + LLNotificationResponderPtr getResponderPtr() { return mResponder; } + +private: + + const LLUUID mId; + LLSD mPayload; + LLSD mSubstitutions; + LLDate mTimestamp; + LLDate mExpiresAt; + bool mCancelled; + bool mRespondedTo; // once the notification has been responded to, this becomes true + LLSD mResponse; + bool mIgnored; + ENotificationPriority mPriority; + LLNotificationFormPtr mForm; + void* mResponderObj; // TODO - refactor/remove this field + LLNotificationResponderPtr mResponder; + bool mOfferFromAgent; + bool mIsDND; + + // a reference to the template + LLNotificationTemplatePtr mTemplatep; + + /* + We want to be able to store and reload notifications so that they can survive + a shutdown/restart of the client. So we can't simply pass in callbacks; + we have to specify a callback mechanism that can be used by name rather than + by some arbitrary pointer -- and then people have to initialize callbacks + in some useful location. So we use LLNotificationFunctorRegistry to manage them. + */ + std::string mResponseFunctorName; + + /* + In cases where we want to specify an explict, non-persisted callback, + we store that in the callback registry under a dynamically generated + key, and store the key in the notification, so we can still look it up + using the same mechanism. + */ + bool mTemporaryResponder; + + // keep track of other notifications combined with COMBINE_WITH_NEW + std::vector mCombinedNotifications; + + void init(const std::string& template_name, const LLSD& form_elements); + + void cancel(); + +public: + LLNotification(const LLSDParamAdapter& p); + + void setResponseFunctor(std::string const &responseFunctorName); + + void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb); + + void setResponseFunctor(const LLNotificationResponderPtr& responder); + + typedef enum e_response_template_type + { + WITHOUT_DEFAULT_BUTTON, + WITH_DEFAULT_BUTTON + } EResponseTemplateType; + + // return response LLSD filled in with default form contents and (optionally) the default button selected + LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON); + + // returns index of first button with value==true + // usually this the button the user clicked on + // returns -1 if no button clicked (e.g. form has not been displayed) + static S32 getSelectedOption(const LLSD& notification, const LLSD& response); + // returns name of first button with value==true + static std::string getSelectedOptionName(const LLSD& notification); + + // after someone responds to a notification (usually by clicking a button, + // but sometimes by filling out a little form and THEN clicking a button), + // the result of the response (the name and value of the button clicked, + // plus any other data) should be packaged up as LLSD, then passed as a + // parameter to the notification's respond() method here. This will look up + // and call the appropriate responder. + // + // response is notification serialized as LLSD: + // ["name"] = notification name + // ["form"] = LLSD tree that includes form description and any prefilled form data + // ["response"] = form data filled in by user + // (including, but not limited to which button they clicked on) + // ["payload"] = transaction specific data, such as ["source_id"] (originator of notification), + // ["item_id"] (attached inventory item), etc. + // ["substitutions"] = string substitutions used to generate notification message + // from the template + // ["time"] = time at which notification was generated; + // ["expiry"] = time at which notification expires; + // ["responseFunctor"] = name of registered functor that handles responses to notification; + LLSD asLLSD(bool excludeTemplateElements = false); + + const LLNotificationFormPtr getForm(); + void updateForm(const LLNotificationFormPtr& form); + + void repost(); + + void respond(const LLSD& sd); + void respondWithDefault(); + + void* getResponder() { return mResponderObj; } + + void setResponder(void* responder) { mResponderObj = responder; } + + void setIgnored(bool ignore); + + bool isCancelled() const + { + return mCancelled; + } + + bool isRespondedTo() const + { + return mRespondedTo; + } + + bool isActive() const + { + return !isRespondedTo() + && !isCancelled() + && !isExpired(); + } + + const LLSD& getResponse() { return mResponse; } + + bool isIgnored() const + { + return mIgnored; + } + + const std::string& getName() const; + + const std::string& getIcon() const; + + bool isPersistent() const; + + const LLUUID& id() const + { + return mId; + } + + const LLSD& getPayload() const + { + return mPayload; + } + + const LLSD& getSubstitutions() const + { + return mSubstitutions; + } + + const LLDate& getDate() const + { + return mTimestamp; + } + + bool getOfferFromAgent() const + { + return mOfferFromAgent; + } + + bool isDND() const + { + return mIsDND; + } + + void setDND(const bool flag) + { + mIsDND = flag; + } + + std::string getType() const; + std::string getMessage() const; + std::string getFooter() const; + std::string getLabel() const; + std::string getURL() const; + S32 getURLOption() const; + S32 getURLOpenExternally() const; //for url responce option + bool getForceUrlsExternal() const; + bool canLogToChat() const; + bool canLogToIM() const; + bool canShowToast() const; + bool canFadeToast() const; + bool hasFormElements() const; + void playSound(); + + typedef enum e_combine_behavior + { + REPLACE_WITH_NEW, + COMBINE_WITH_NEW, + KEEP_OLD, + CANCEL_OLD + + } ECombineBehavior; + + ECombineBehavior getCombineBehavior() const; + + const LLDate getExpiration() const + { + return mExpiresAt; + } + + ENotificationPriority getPriority() const + { + return mPriority; + } + + const LLUUID getID() const + { + return mId; + } + + // comparing two notifications normally means comparing them by UUID (so we can look them + // up quickly this way) + bool operator<(const LLNotification& rhs) const + { + return mId < rhs.mId; + } + + bool operator==(const LLNotification& rhs) const + { + return mId == rhs.mId; + } + + bool operator!=(const LLNotification& rhs) const + { + return !operator==(rhs); + } + + bool isSameObjectAs(const LLNotification* rhs) const + { + return this == rhs; + } + + // this object has been updated, so tell all our clients + void update(); + + void updateFrom(LLNotificationPtr other); + + // A fuzzy equals comparator. + // true only if both notifications have the same template and + // 1) flagged as unique (there can be only one of these) OR + // 2) all required payload fields of each also exist in the other. + bool isEquivalentTo(LLNotificationPtr that) const; + + // if the current time is greater than the expiration, the notification is expired + bool isExpired() const + { + if (mExpiresAt.secondsSinceEpoch() == 0) + { + return false; + } + + LLDate rightnow = LLDate::now(); + return rightnow > mExpiresAt; + } + + std::string summarize() const; + + bool hasUniquenessConstraints() const; + + bool matchesTag(const std::string& tag); + + virtual ~LLNotification() {} +}; + +std::ostream& operator<<(std::ostream& s, const LLNotification& notification); + +namespace LLNotificationFilters +{ + // a sample filter + bool includeEverything(LLNotificationPtr p); + + typedef enum e_comparison + { + EQUAL, + LESS, + GREATER, + LESS_EQUAL, + GREATER_EQUAL + } EComparison; + + // generic filter functor that takes method or member variable reference + template + struct filterBy + { + typedef boost::function field_t; + typedef typename boost::remove_reference::type value_t; + + filterBy(field_t field, value_t value, EComparison comparison = EQUAL) + : mField(field), + mFilterValue(value), + mComparison(comparison) + { + } + + bool operator()(LLNotificationPtr p) + { + switch(mComparison) + { + case EQUAL: + return mField(p) == mFilterValue; + case LESS: + return mField(p) < mFilterValue; + case GREATER: + return mField(p) > mFilterValue; + case LESS_EQUAL: + return mField(p) <= mFilterValue; + case GREATER_EQUAL: + return mField(p) >= mFilterValue; + default: + return false; + } + } + + field_t mField; + value_t mFilterValue; + EComparison mComparison; + }; +}; + +namespace LLNotificationComparators +{ + struct orderByUUID + { + bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) const + { + return lhs->id() < rhs->id(); + } + }; +}; + +typedef boost::function LLNotificationFilter; +typedef std::set LLNotificationSet; +typedef std::multimap LLNotificationMap; + +// ======================================================== +// Abstract base class (interface) for a channel; also used for the master container. +// This lets us arrange channels into a call hierarchy. + +// We maintain a hierarchy of notification channels; events are always started at the top +// and propagated through the hierarchy only if they pass a filter. +// Any channel can be created with a parent. A null parent (empty string) means it's +// tied to the root of the tree (the LLNotifications class itself). +// The default hierarchy looks like this: +// +// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History +// +-- Alerts +// +-- Notifications +// +// In general, new channels that want to only see notifications that pass through +// all of the built-in tests should attach to the "Visible" channel +// +class LLNotificationChannelBase : + public LLEventTrackable, + public LLRefCount +{ + LOG_CLASS(LLNotificationChannelBase); +public: + LLNotificationChannelBase(LLNotificationFilter filter) + : mFilter(filter) + , mItems() + , mItemsMutex() + {} + + virtual ~LLNotificationChannelBase() + { + // explicit cleanup for easier issue detection + mChanged.disconnect_all_slots(); + mPassedFilter.disconnect_all_slots(); + mFailedFilter.disconnect_all_slots(); + LLMutexLock lock(&mItemsMutex); + mItems.clear(); + } + // you can also connect to a Channel, so you can be notified of + // changes to this channel + LLBoundListener connectChanged(const LLEventListener& slot) + { + // Call this->connectChangedImpl() to actually connect it. + return connectChangedImpl(slot); + } + LLBoundListener connectAtFrontChanged(const LLEventListener& slot) + { + return connectAtFrontChangedImpl(slot); + } + LLBoundListener connectPassedFilter(const LLEventListener& slot) + { + // see comments in connectChanged() + return connectPassedFilterImpl(slot); + } + LLBoundListener connectFailedFilter(const LLEventListener& slot) + { + // see comments in connectChanged() + return connectFailedFilterImpl(slot); + } + + // use this when items change or to add a new one + bool updateItem(const LLSD& payload); + const LLNotificationFilter& getFilter() { return mFilter; } + +protected: + LLBoundListener connectChangedImpl(const LLEventListener& slot); + LLBoundListener connectAtFrontChangedImpl(const LLEventListener& slot); + LLBoundListener connectPassedFilterImpl(const LLEventListener& slot); + LLBoundListener connectFailedFilterImpl(const LLEventListener& slot); + + LLNotificationSet mItems; + LLStandardSignal mChanged; + LLStandardSignal mPassedFilter; + LLStandardSignal mFailedFilter; + LLMutex mItemsMutex; + + // these are action methods that subclasses can override to take action + // on specific types of changes; the management of the mItems list is + // still handled by the generic handler. + virtual void onLoad(LLNotificationPtr p) {} + virtual void onAdd(LLNotificationPtr p) {} + virtual void onDelete(LLNotificationPtr p) {} + virtual void onChange(LLNotificationPtr p) {} + + virtual void onFilterPass(LLNotificationPtr p) {} + virtual void onFilterFail(LLNotificationPtr p) {} + + bool updateItem(const LLSD& payload, LLNotificationPtr pNotification); + LLNotificationFilter mFilter; +}; + +// The type of the pointers that we're going to manage in the NotificationQueue system +// Because LLNotifications is a singleton, we don't actually expect to ever +// destroy it, but if it becomes necessary to do so, the shared_ptr model +// will ensure that we don't leak resources. +class LLNotificationChannel; +typedef boost::intrusive_ptr LLNotificationChannelPtr; + +// manages a list of notifications +// Note that if this is ever copied around, we might find ourselves with multiple copies +// of a queue with notifications being added to different nonequivalent copies. So we +// make it inherit from boost::noncopyable, and then create a map of LLPointer to manage it. +// +class LLNotificationChannel : + boost::noncopyable, + public LLNotificationChannelBase, + public LLInstanceTracker +{ + LOG_CLASS(LLNotificationChannel); + +public: + // Notification Channels have a filter, which determines which notifications + // will be added to this channel. + // Channel filters cannot change. + struct Params : public LLInitParam::Block + { + Mandatory name; + Optional filter; + Multiple sources; + }; + + LLNotificationChannel(const Params& p = Params()); + LLNotificationChannel(const std::string& name, const std::string& parent, LLNotificationFilter filter); + + virtual ~LLNotificationChannel(); + typedef LLNotificationSet::iterator Iterator; + + std::string getName() const { return mName; } + typedef std::vector::const_iterator parents_iter; + boost::iterator_range getParents() const + { + return boost::iterator_range(mParents); + } + + bool isEmpty() const; + S32 size() const; + size_t size(); + + typedef boost::function NotificationProcess; + void forEachNotification(NotificationProcess process); + + std::string summarize(); + +protected: + void connectToChannel(const std::string& channel_name); + +private: + std::string mName; + std::vector mParents; + std::vector mListeners; +}; + +// An interface class to provide a clean linker seam to the LLNotifications class. +// Extend this interface as needed for your use of LLNotifications. +class LLNotificationsInterface +{ +public: + virtual LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + LLNotificationFunctorRegistry::ResponseFunctor functor) = 0; +}; + +class LLNotifications : + public LLNotificationsInterface, + public LLSingleton, + public LLNotificationChannelBase +{ + LLSINGLETON(LLNotifications); + LOG_CLASS(LLNotifications); + virtual ~LLNotifications() {} + +public: + + // Needed to clear up RefCounted things prior to actual destruction + // as the singleton nature of the class makes them do "bad things" + // on at least Mac, if not all 3 platforms + // + void clear(); + + // load all notification descriptions from file + // calling more than once will overwrite existing templates + // but never delete a template + bool loadTemplates(); + + // load visibility rules from file; + // OK to call more than once because it will reload + bool loadVisibilityRules(); + + // Add a simple notification (from XUI) + void addFromCallback(const LLSD& name); + + // *NOTE: To add simple notifications, #include "llnotificationsutil.h" + // and use LLNotificationsUtil::add("MyNote") or add("MyNote", args) + LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload); + LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + const std::string& functor_name); + /* virtual */ LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + LLNotificationFunctorRegistry::ResponseFunctor functor) override; + LLNotificationPtr add(const LLNotification::Params& p); + + void add(const LLNotificationPtr pNotif); + void load(const LLNotificationPtr pNotif); + void cancel(LLNotificationPtr pNotif); + void cancelByName(const std::string& name); + void cancelByOwner(const LLUUID ownerId); + void update(const LLNotificationPtr pNotif); + + LLNotificationPtr find(LLUUID uuid); + + // This is all stuff for managing the templates + // take your template out + LLNotificationTemplatePtr getTemplate(const std::string& name); + + // get the whole collection + typedef std::vector TemplateNames; + TemplateNames getTemplateNames() const; // returns a list of notification names + + typedef std::map TemplateMap; + + TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); } + TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); } + + // test for existence + bool templateExists(const std::string& name); + + typedef std::list VisibilityRuleList; + + void forceResponse(const LLNotification::Params& params, S32 option); + + void createDefaultChannels(); + + LLNotificationChannelPtr getChannel(const std::string& channelName); + + std::string getGlobalString(const std::string& key) const; + + void setIgnoreAllNotifications(bool ignore); + bool getIgnoreAllNotifications(); + + void setIgnored(const std::string& name, bool ignored); + bool getIgnored(const std::string& name); + + bool isVisibleByRules(LLNotificationPtr pNotification); + +private: + /*virtual*/ void initSingleton() override; + /*virtual*/ void cleanupSingleton() override; + + void loadPersistentNotifications(); + + bool expirationFilter(LLNotificationPtr pNotification); + bool expirationHandler(const LLSD& payload); + bool uniqueFilter(LLNotificationPtr pNotification); + bool uniqueHandler(const LLSD& payload); + bool failedUniquenessTest(const LLSD& payload); + LLNotificationChannelPtr pHistoryChannel; + LLNotificationChannelPtr pExpirationChannel; + + TemplateMap mTemplates; + + VisibilityRuleList mVisibilityRules; + + std::string mFileName; + + LLNotificationMap mUniqueNotifications; + + typedef std::map GlobalStringMap; + GlobalStringMap mGlobalStrings; + + bool mIgnoreAllNotifications; + + std::unique_ptr mListener; + + std::vector mDefaultChannels; +}; + +/** + * Abstract class for postponed notifications. + * Provides possibility to add notification after specified by id avatar or group will be + * received from cache name. The object of this type automatically well be deleted + * by cleanup method after respond will be received from cache name. + * + * To add custom postponed notification to the notification system client should: + * 1 create class derived from LLPostponedNotification; + * 2 call LLPostponedNotification::add method; + */ +class LLPostponedNotification : public LLMortician +{ +public: + /** + * Performs hooking cache name callback which will add notification to notifications system. + * Type of added notification should be specified by template parameter T + * and non-private derived from LLPostponedNotification class, + * otherwise compilation error will occur. + */ + template + static void add(const LLNotification::Params& params, + const LLUUID& id, bool is_group) + { + // upcast T to the base type to restrict T derivation from LLPostponedNotification + LLPostponedNotification* thiz = new T(); + thiz->mParams = params; + + // Avoid header file dependency on llcachename.h + thiz->lookupName(id, is_group); + } + +private: + void lookupName(const LLUUID& id, bool is_group); + // only used for groups + void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group); + // only used for avatars + void fetchAvatarName(const LLUUID& id); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + // used for both group and avatar names + void finalizeName(const std::string& name); + + void cleanup() + { + die(); + } + +protected: + LLPostponedNotification() + : mParams(), + mName(), + mAvatarNameCacheConnection() + {} + + virtual ~LLPostponedNotification() + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + } + + /** + * Abstract method provides possibility to modify notification parameters and + * will be called after cache name retrieve information about avatar or group + * and before notification will be added to the notification system. + */ + virtual void modifyNotificationParams() = 0; + + LLNotification::Params mParams; + std::string mName; + boost::signals2::connection mAvatarNameCacheConnection; +}; + +// Stores only persistent notifications. +// Class users can use connectChanged() to process persistent notifications +// (see LLPersistentNotificationStorage for example). +class LLPersistentNotificationChannel : public LLNotificationChannel +{ + LOG_CLASS(LLPersistentNotificationChannel); +public: + LLPersistentNotificationChannel() + : LLNotificationChannel("Persistent", "Visible", ¬ificationFilter) + {} + + virtual ~LLPersistentNotificationChannel() + { + mHistory.clear(); + } + + typedef std::vector history_list_t; + history_list_t::iterator beginHistory() { sortHistory(); return mHistory.begin(); } + history_list_t::iterator endHistory() { return mHistory.end(); } + +private: + struct sortByTime + { + S32 operator ()(const LLNotificationPtr& a, const LLNotificationPtr& b) + { + return a->getDate() < b->getDate(); + } + }; + + void sortHistory() + { + std::sort(mHistory.begin(), mHistory.end(), sortByTime()); + } + + // The channel gets all persistent notifications except those that have been canceled + static bool notificationFilter(LLNotificationPtr pNotification) + { + bool handle_notification = false; + + handle_notification = pNotification->isPersistent() + && !pNotification->isCancelled(); + + return handle_notification; + } + + void onAdd(LLNotificationPtr p) + { + mHistory.push_back(p); + } + + void onLoad(LLNotificationPtr p) + { + mHistory.push_back(p); + } + + std::vector mHistory; +}; + +#endif//LL_LLNOTIFICATIONS_H + diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index f6758133f3..468cdb10fb 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -1,875 +1,875 @@ -/** - * @file llpanel.cpp - * @brief LLPanel base class - * - * $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$ - */ - -// Opaque view with a background and a border. Can contain LLUICtrls. - -#include "linden_common.h" - -#define LLPANEL_CPP -#include "llpanel.h" - -#include "llfocusmgr.h" -#include "llfontgl.h" -#include "llrect.h" -#include "llerror.h" -#include "lldir.h" -#include "lltimer.h" - -#include "llbutton.h" -#include "llmenugl.h" -#include "llui.h" -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llcontrol.h" -#include "lltextbox.h" -#include "lluictrl.h" -#include "lluictrlfactory.h" -#include "llviewborder.h" - -static LLDefaultChildRegistry::Register r1("panel", &LLPanel::fromXML); -LLPanel::factory_stack_t LLPanel::sFactoryStack; - - -// Compiler optimization, generate extern template -template class LLPanel* LLView::getChild( - const std::string& name, bool recurse) const; - -LLPanel::LocalizedString::LocalizedString() -: name("name"), - value("value") -{} - -const LLPanel::Params& LLPanel::getDefaultParams() -{ - return LLUICtrlFactory::getDefaultParams(); -} - -LLPanel::Params::Params() -: has_border("border", false), - border(""), - background_visible("background_visible", false), - background_opaque("background_opaque", false), - bg_opaque_color("bg_opaque_color"), - bg_alpha_color("bg_alpha_color"), - bg_opaque_image_overlay("bg_opaque_image_overlay"), - bg_alpha_image_overlay("bg_alpha_image_overlay"), - bg_opaque_image("bg_opaque_image"), - bg_alpha_image("bg_alpha_image"), - min_width("min_width", 100), - min_height("min_height", 100), - strings("string"), - filename("filename"), - class_name("class"), - help_topic("help_topic"), - visible_callback("visible_callback"), - accepts_badge("accepts_badge") -{ - addSynonym(background_visible, "bg_visible"); - addSynonym(has_border, "border_visible"); - addSynonym(label, "title"); -} - - -LLPanel::LLPanel(const LLPanel::Params& p) -: LLUICtrl(p), - LLBadgeHolder(p.accepts_badge), - mBgVisible(p.background_visible), - mBgOpaque(p.background_opaque), - mBgOpaqueColor(p.bg_opaque_color()), - mBgAlphaColor(p.bg_alpha_color()), - mBgOpaqueImageOverlay(p.bg_opaque_image_overlay), - mBgAlphaImageOverlay(p.bg_alpha_image_overlay), - mBgOpaqueImage(p.bg_opaque_image()), - mBgAlphaImage(p.bg_alpha_image()), - mDefaultBtn(NULL), - mBorder(NULL), - mLabel(p.label), - mHelpTopic(p.help_topic), - mCommitCallbackRegistrar(false), - mEnableCallbackRegistrar(false), - mXMLFilename(p.filename), - mVisibleSignal(NULL) - // *NOTE: Be sure to also change LLPanel::initFromParams(). We have too - // many classes derived from LLPanel to retrofit them all to pass in params. -{ - if (p.has_border) - { - addBorder(p.border); - } -} - -LLPanel::~LLPanel() -{ - delete mVisibleSignal; -} - -// virtual -bool LLPanel::isPanel() const -{ - return true; -} - -void LLPanel::addBorder(LLViewBorder::Params p) -{ - removeBorder(); - p.rect = getLocalRect(); - - mBorder = LLUICtrlFactory::create(p); - addChild( mBorder ); -} - -void LLPanel::addBorder() -{ - LLViewBorder::Params p; - p.border_thickness(LLPANEL_BORDER_WIDTH); - addBorder(p); -} - - -void LLPanel::removeBorder() -{ - if (mBorder) - { - removeChild(mBorder); - delete mBorder; - mBorder = NULL; - } -} - - -// virtual -void LLPanel::clearCtrls() -{ - LLPanel::ctrl_list_t ctrls = getCtrlList(); - for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it) - { - LLUICtrl* ctrl = *ctrl_it; - ctrl->setFocus( false ); - ctrl->setEnabled( false ); - ctrl->clear(); - } -} - -void LLPanel::setCtrlsEnabled( bool b ) -{ - LLPanel::ctrl_list_t ctrls = getCtrlList(); - for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it) - { - LLUICtrl* ctrl = *ctrl_it; - ctrl->setEnabled( b ); - } -} - -LLPanel::ctrl_list_t LLPanel::getCtrlList() const -{ - ctrl_list_t controls; - for(child_list_t::const_iterator it = getChildList()->begin(), end_it = getChildList()->end(); it != end_it; ++it) - { - LLView* viewp = *it; - if(viewp->isCtrl()) - { - controls.push_back(static_cast(viewp)); - } - } - return controls; -} - - -void LLPanel::draw() -{ - F32 alpha = getDrawContext().mAlpha; - - // draw background - if( mBgVisible ) - { - alpha = getCurrentTransparency(); - - LLRect local_rect = getLocalRect(); - if (mBgOpaque ) - { - // opaque, in-front look - if (mBgOpaqueImage.notNull()) - { - mBgOpaqueImage->draw( local_rect, mBgOpaqueImageOverlay % alpha ); - } - else - { - // fallback to flat colors when there are no images - gl_rect_2d( local_rect, mBgOpaqueColor.get() % alpha); - } - } - else - { - // transparent, in-back look - if (mBgAlphaImage.notNull()) - { - mBgAlphaImage->draw( local_rect, mBgAlphaImageOverlay % alpha ); - } - else - { - gl_rect_2d( local_rect, mBgAlphaColor.get() % alpha ); - } - } - } - - updateDefaultBtn(); - - LLView::draw(); -} - -void LLPanel::updateDefaultBtn() -{ - if( mDefaultBtn) - { - if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled()) - { - LLButton* buttonp = dynamic_cast(gFocusMgr.getKeyboardFocus()); - bool focus_is_child_button = buttonp && buttonp->getCommitOnReturn(); - // only enable default button when current focus is not a return-capturing button - mDefaultBtn->setBorderEnabled(!focus_is_child_button); - } - else - { - mDefaultBtn->setBorderEnabled(false); - } - } -} - -void LLPanel::refresh() -{ - // do nothing by default - // but is automatically called in setFocus(true) -} - -void LLPanel::setDefaultBtn(LLButton* btn) -{ - if (mDefaultBtn && mDefaultBtn->getEnabled()) - { - mDefaultBtn->setBorderEnabled(false); - } - mDefaultBtn = btn; - if (mDefaultBtn) - { - mDefaultBtn->setBorderEnabled(true); - } -} - -void LLPanel::setDefaultBtn(const std::string& id) -{ - LLButton *button = getChild(id); - if (button) - { - setDefaultBtn(button); - } - else - { - setDefaultBtn(NULL); - } -} - -bool LLPanel::handleKeyHere( KEY key, MASK mask ) -{ - bool handled = false; - - LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); - - // handle user hitting ESC to defocus - if (key == KEY_ESCAPE) - { - setFocus(false); - return true; - } - else if( (mask == MASK_SHIFT) && (KEY_TAB == key)) - { - //SHIFT-TAB - if (cur_focus) - { - LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); - if (focus_root) - { - handled = focus_root->focusPrevItem(false); - } - } - } - else if( (mask == MASK_NONE ) && (KEY_TAB == key)) - { - //TAB - if (cur_focus) - { - LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); - if (focus_root) - { - handled = focus_root->focusNextItem(false); - } - } - } - - // If RETURN was pressed and something has focus, call onCommit() - if (!handled && cur_focus && key == KEY_RETURN && mask == MASK_NONE) - { - LLButton* focused_button = dynamic_cast(cur_focus); - if (focused_button && focused_button->getCommitOnReturn()) - { - // current focus is a return-capturing button, - // let *that* button handle the return key - handled = false; - } - else if (mDefaultBtn && mDefaultBtn->getVisible() && mDefaultBtn->getEnabled()) - { - // If we have a default button, click it when return is pressed - mDefaultBtn->onCommit(); - handled = true; - } - else if (cur_focus->acceptsTextInput()) - { - // call onCommit for text input handling control - cur_focus->onCommit(); - handled = true; - } - } - - return handled; -} - -void LLPanel::onVisibilityChange ( bool new_visibility ) -{ - LLUICtrl::onVisibilityChange ( new_visibility ); - if (mVisibleSignal) - (*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass bool as LLSD -} - -void LLPanel::setFocus(bool b) -{ - if( b && !hasFocus()) - { - // give ourselves focus preemptively, to avoid infinite loop - LLUICtrl::setFocus(true); - // then try to pass to first valid child - focusFirstItem(); - } - else - { - LLUICtrl::setFocus(b); - } -} - -void LLPanel::setBorderVisible(bool b) -{ - if (mBorder) - { - mBorder->setVisible( b ); - } -} - -LLTrace::BlockTimerStatHandle FTM_PANEL_CONSTRUCTION("Panel Construction"); - -LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_node) -{ - std::string name("panel"); - node->getAttributeString("name", name); - - std::string class_attr; - node->getAttributeString("class", class_attr); - - LLPanel* panelp = NULL; - - { LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION); - - if(!class_attr.empty()) - { - panelp = LLRegisterPanelClass::instance().createPanelClass(class_attr); - if (!panelp) - { - LL_WARNS() << "Panel class \"" << class_attr << "\" not registered." << LL_ENDL; - } - } - - if (!panelp) - { - panelp = createFactoryPanel(name); - llassert(panelp); - - if (!panelp) - { - return NULL; // :( - } - } - - } - // factory panels may have registered their own factory maps - if (!panelp->getFactoryMap().empty()) - { - sFactoryStack.push_back(&panelp->getFactoryMap()); - } - // for local registry callbacks; define in constructor, referenced in XUI or postBuild - panelp->mCommitCallbackRegistrar.pushScope(); - panelp->mEnableCallbackRegistrar.pushScope(); - - panelp->initPanelXML(node, parent, output_node, LLUICtrlFactory::getDefaultParams()); - - panelp->mCommitCallbackRegistrar.popScope(); - panelp->mEnableCallbackRegistrar.popScope(); - - if (!panelp->getFactoryMap().empty()) - { - sFactoryStack.pop_back(); - } - - return panelp; -} - -void LLPanel::initFromParams(const LLPanel::Params& p) -{ - //setting these here since panel constructor not called with params - //and LLView::initFromParams will use them to set visible and enabled - setVisible(p.visible); - setEnabled(p.enabled); - setFocusRoot(p.focus_root); - setSoundFlags(p.sound_flags); - - // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible - LLUICtrl::initFromParams(p); - - // visible callback - if (p.visible_callback.isProvided()) - { - setVisibleCallback(initCommitCallback(p.visible_callback)); - } - - for (LLInitParam::ParamIterator::const_iterator it = p.strings.begin(); - it != p.strings.end(); - ++it) - { - mUIStrings[it->name] = it->value; - } - - setLabel(p.label()); - setHelpTopic(p.help_topic); - setShape(p.rect); - parseFollowsFlags(p); - - setToolTip(p.tool_tip()); - setFromXUI(p.from_xui); - - mHoverCursor = getCursorFromString(p.hover_cursor); - - if (p.has_border) - { - addBorder(p.border); - } - // let constructors set this value if not provided - if (p.use_bounding_rect.isProvided()) - { - setUseBoundingRect(p.use_bounding_rect); - } - setDefaultTabGroup(p.default_tab_group); - setMouseOpaque(p.mouse_opaque); - - setBackgroundVisible(p.background_visible); - setBackgroundOpaque(p.background_opaque); - setBackgroundColor(p.bg_opaque_color().get()); - setTransparentColor(p.bg_alpha_color().get()); - mBgOpaqueImage = p.bg_opaque_image(); - mBgAlphaImage = p.bg_alpha_image(); - mBgOpaqueImageOverlay = p.bg_opaque_image_overlay; - mBgAlphaImageOverlay = p.bg_alpha_image_overlay; - - setAcceptsBadge(p.accepts_badge); -} - -static LLTrace::BlockTimerStatHandle FTM_PANEL_SETUP("Panel Setup"); -static LLTrace::BlockTimerStatHandle FTM_EXTERNAL_PANEL_LOAD("Load Extern Panel Reference"); -static LLTrace::BlockTimerStatHandle FTM_PANEL_POSTBUILD("Panel PostBuild"); - -bool LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params) -{ - Params params(default_params); - { - LL_RECORD_BLOCK_TIME(FTM_PANEL_SETUP); - - LLXMLNodePtr referenced_xml; - std::string xml_filename = mXMLFilename; - - // if the panel didn't provide a filename, check the node - if (xml_filename.empty()) - { - node->getAttributeString("filename", xml_filename); - setXMLFilename(xml_filename); - } - - LLXUIParser parser; - - if (!xml_filename.empty()) - { - if (output_node) - { - //if we are exporting, we want to export the current xml - //not the referenced xml - parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName()); - Params output_params(params); - setupParamsForExport(output_params, parent); - output_node->setName(node->getName()->mString); - parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); - return true; - } - - LLUICtrlFactory::instance().pushFileName(xml_filename); - - LL_RECORD_BLOCK_TIME(FTM_EXTERNAL_PANEL_LOAD); - if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml)) - { - LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL; - - return false; - } - - parser.readXUI(referenced_xml, params, LLUICtrlFactory::getInstance()->getCurFileName()); - - // add children using dimensions from referenced xml for consistent layout - setShape(params.rect); - LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance()); - - LLUICtrlFactory::instance().popFileName(); - } - - // ask LLUICtrlFactory for filename, since xml_filename might be empty - parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName()); - - if (output_node) - { - Params output_params(params); - setupParamsForExport(output_params, parent); - output_node->setName(node->getName()->mString); - parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); - } - - params.from_xui = true; - applyXUILayout(params, parent); - { - LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION); - initFromParams(params); - } - - // add children - LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node); - - // Connect to parent after children are built, because tab containers - // do a reshape() on their child panels, which requires that the children - // be built/added. JC - if (parent) - { - S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : parent->getLastTabGroup(); - parent->addChild(this, tab_group); - } - - { - LL_RECORD_BLOCK_TIME(FTM_PANEL_POSTBUILD); - postBuild(); - } - } - return true; -} - -bool LLPanel::hasString(const std::string& name) -{ - return mUIStrings.find(name) != mUIStrings.end(); -} - -std::string LLPanel::getString(const std::string& name, const LLStringUtil::format_map_t& args) const -{ - ui_string_map_t::const_iterator found_it = mUIStrings.find(name); - if (found_it != mUIStrings.end()) - { - // make a copy as format works in place - LLUIString formatted_string = LLUIString(found_it->second); - formatted_string.setArgList(args); - return formatted_string.getString(); - } - std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate - if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode")) - { - LL_ERRS() << err_str << LL_ENDL; - } - else - { - LL_WARNS() << err_str << LL_ENDL; - } - return LLStringUtil::null; -} - -std::string LLPanel::getString(const std::string& name) const -{ - ui_string_map_t::const_iterator found_it = mUIStrings.find(name); - if (found_it != mUIStrings.end()) - { - return found_it->second; - } - std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate - if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode")) - { - LL_ERRS() << err_str << LL_ENDL; - } - else - { - LL_WARNS() << err_str << LL_ENDL; - } - return LLStringUtil::null; -} - - -void LLPanel::childSetVisible(const std::string& id, bool visible) -{ - LLView* child = findChild(id); - if (child) - { - child->setVisible(visible); - } -} - -void LLPanel::childSetEnabled(const std::string& id, bool enabled) -{ - LLView* child = findChild(id); - if (child) - { - child->setEnabled(enabled); - } -} - -void LLPanel::childSetFocus(const std::string& id, bool focus) -{ - LLUICtrl* child = findChild(id); - if (child) - { - child->setFocus(focus); - } -} - -bool LLPanel::childHasFocus(const std::string& id) -{ - LLUICtrl* child = findChild(id); - if (child) - { - return child->hasFocus(); - } - else - { - return false; - } -} - -// *TODO: Deprecate; for backwards compatability only: -// Prefer getChild("foo")->setCommitCallback(boost:bind(...)), -// which takes a generic slot. Or use mCommitCallbackRegistrar.add() with -// a named callback and reference it in XML. -void LLPanel::childSetCommitCallback(const std::string& id, boost::function cb, void* data) -{ - LLUICtrl* child = findChild(id); - if (child) - { - child->setCommitCallback(boost::bind(cb, child, data)); - } -} - -void LLPanel::childSetColor(const std::string& id, const LLColor4& color) -{ - LLUICtrl* child = findChild(id); - if (child) - { - child->setColor(color); - } -} - -LLCtrlSelectionInterface* LLPanel::childGetSelectionInterface(const std::string& id) const -{ - LLUICtrl* child = findChild(id); - if (child) - { - return child->getSelectionInterface(); - } - return NULL; -} - -LLCtrlListInterface* LLPanel::childGetListInterface(const std::string& id) const -{ - LLUICtrl* child = findChild(id); - if (child) - { - return child->getListInterface(); - } - return NULL; -} - -LLCtrlScrollInterface* LLPanel::childGetScrollInterface(const std::string& id) const -{ - LLUICtrl* child = findChild(id); - if (child) - { - return child->getScrollInterface(); - } - return NULL; -} - -void LLPanel::childSetValue(const std::string& id, LLSD value) -{ - LLUICtrl* child = findChild(id); - if (child) - { - child->setValue(value); - } -} - -LLSD LLPanel::childGetValue(const std::string& id) const -{ - LLUICtrl* child = findChild(id); - if (child) - { - return child->getValue(); - } - // Not found => return undefined - return LLSD(); -} - -bool LLPanel::childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text) -{ - LLUICtrl* child = findChild(id); - if (child) - { - return child->setTextArg(key, text); - } - return false; -} - -bool LLPanel::childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text) -{ - LLView* child = findChild(id); - if (child) - { - return child->setLabelArg(key, text); - } - return false; -} - -void LLPanel::childSetAction(const std::string& id, const commit_signal_t::slot_type& function) -{ - LLButton* button = findChild(id); - if (button) - { - button->setClickedCallback(function); - } -} - -void LLPanel::childSetAction(const std::string& id, boost::function function, void* value) -{ - LLButton* button = findChild(id); - if (button) - { - button->setClickedCallback(boost::bind(function, value)); - } -} - -boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mVisibleSignal) - { - mVisibleSignal = new commit_signal_t(); - } - - return mVisibleSignal->connect(cb); -} - -//----------------------------------------------------------------------------- -// buildPanel() -//----------------------------------------------------------------------------- -bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) -{ - LL_PROFILE_ZONE_SCOPED; - bool didPost = false; - LLXMLNodePtr root; - - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) - { - LL_WARNS() << "Couldn't parse panel from: " << filename << LL_ENDL; - return didPost; - } - - // root must be called panel - if( !root->hasName("panel" ) ) - { - LL_WARNS() << "Root node should be named panel in : " << filename << LL_ENDL; - return didPost; - } - - LL_DEBUGS() << "Building panel " << filename << LL_ENDL; - - LLUICtrlFactory::instance().pushFileName(filename); - { - if (!getFactoryMap().empty()) - { - sFactoryStack.push_back(&getFactoryMap()); - } - - // for local registry callbacks; define in constructor, referenced in XUI or postBuild - getCommitCallbackRegistrar().pushScope(); - getEnableCallbackRegistrar().pushScope(); - - didPost = initPanelXML(root, NULL, NULL, default_params); - - getCommitCallbackRegistrar().popScope(); - getEnableCallbackRegistrar().popScope(); - - setXMLFilename(filename); - - if (!getFactoryMap().empty()) - { - sFactoryStack.pop_back(); - } - } - LLUICtrlFactory::instance().popFileName(); - return didPost; -} - -//----------------------------------------------------------------------------- -// createFactoryPanel() -//----------------------------------------------------------------------------- -LLPanel* LLPanel::createFactoryPanel(const std::string& name) -{ - std::deque::iterator itor; - for (itor = sFactoryStack.begin(); itor != sFactoryStack.end(); ++itor) - { - const LLCallbackMap::map_t* factory_map = *itor; - - // Look up this panel's name in the map. - LLCallbackMap::map_const_iter_t iter = factory_map->find( name ); - if (iter != factory_map->end()) - { - // Use the factory to create the panel, instead of using a default LLPanel. - LLPanel *ret = (LLPanel*) iter->second.mCallback( iter->second.mData ); - return ret; - } - } - LLPanel::Params panel_p; - return LLUICtrlFactory::create(panel_p); -} +/** + * @file llpanel.cpp + * @brief LLPanel base class + * + * $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$ + */ + +// Opaque view with a background and a border. Can contain LLUICtrls. + +#include "linden_common.h" + +#define LLPANEL_CPP +#include "llpanel.h" + +#include "llfocusmgr.h" +#include "llfontgl.h" +#include "llrect.h" +#include "llerror.h" +#include "lldir.h" +#include "lltimer.h" + +#include "llbutton.h" +#include "llmenugl.h" +#include "llui.h" +#include "llkeyboard.h" +#include "lllineeditor.h" +#include "llcontrol.h" +#include "lltextbox.h" +#include "lluictrl.h" +#include "lluictrlfactory.h" +#include "llviewborder.h" + +static LLDefaultChildRegistry::Register r1("panel", &LLPanel::fromXML); +LLPanel::factory_stack_t LLPanel::sFactoryStack; + + +// Compiler optimization, generate extern template +template class LLPanel* LLView::getChild( + const std::string& name, bool recurse) const; + +LLPanel::LocalizedString::LocalizedString() +: name("name"), + value("value") +{} + +const LLPanel::Params& LLPanel::getDefaultParams() +{ + return LLUICtrlFactory::getDefaultParams(); +} + +LLPanel::Params::Params() +: has_border("border", false), + border(""), + background_visible("background_visible", false), + background_opaque("background_opaque", false), + bg_opaque_color("bg_opaque_color"), + bg_alpha_color("bg_alpha_color"), + bg_opaque_image_overlay("bg_opaque_image_overlay"), + bg_alpha_image_overlay("bg_alpha_image_overlay"), + bg_opaque_image("bg_opaque_image"), + bg_alpha_image("bg_alpha_image"), + min_width("min_width", 100), + min_height("min_height", 100), + strings("string"), + filename("filename"), + class_name("class"), + help_topic("help_topic"), + visible_callback("visible_callback"), + accepts_badge("accepts_badge") +{ + addSynonym(background_visible, "bg_visible"); + addSynonym(has_border, "border_visible"); + addSynonym(label, "title"); +} + + +LLPanel::LLPanel(const LLPanel::Params& p) +: LLUICtrl(p), + LLBadgeHolder(p.accepts_badge), + mBgVisible(p.background_visible), + mBgOpaque(p.background_opaque), + mBgOpaqueColor(p.bg_opaque_color()), + mBgAlphaColor(p.bg_alpha_color()), + mBgOpaqueImageOverlay(p.bg_opaque_image_overlay), + mBgAlphaImageOverlay(p.bg_alpha_image_overlay), + mBgOpaqueImage(p.bg_opaque_image()), + mBgAlphaImage(p.bg_alpha_image()), + mDefaultBtn(NULL), + mBorder(NULL), + mLabel(p.label), + mHelpTopic(p.help_topic), + mCommitCallbackRegistrar(false), + mEnableCallbackRegistrar(false), + mXMLFilename(p.filename), + mVisibleSignal(NULL) + // *NOTE: Be sure to also change LLPanel::initFromParams(). We have too + // many classes derived from LLPanel to retrofit them all to pass in params. +{ + if (p.has_border) + { + addBorder(p.border); + } +} + +LLPanel::~LLPanel() +{ + delete mVisibleSignal; +} + +// virtual +bool LLPanel::isPanel() const +{ + return true; +} + +void LLPanel::addBorder(LLViewBorder::Params p) +{ + removeBorder(); + p.rect = getLocalRect(); + + mBorder = LLUICtrlFactory::create(p); + addChild( mBorder ); +} + +void LLPanel::addBorder() +{ + LLViewBorder::Params p; + p.border_thickness(LLPANEL_BORDER_WIDTH); + addBorder(p); +} + + +void LLPanel::removeBorder() +{ + if (mBorder) + { + removeChild(mBorder); + delete mBorder; + mBorder = NULL; + } +} + + +// virtual +void LLPanel::clearCtrls() +{ + LLPanel::ctrl_list_t ctrls = getCtrlList(); + for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it) + { + LLUICtrl* ctrl = *ctrl_it; + ctrl->setFocus( false ); + ctrl->setEnabled( false ); + ctrl->clear(); + } +} + +void LLPanel::setCtrlsEnabled( bool b ) +{ + LLPanel::ctrl_list_t ctrls = getCtrlList(); + for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it) + { + LLUICtrl* ctrl = *ctrl_it; + ctrl->setEnabled( b ); + } +} + +LLPanel::ctrl_list_t LLPanel::getCtrlList() const +{ + ctrl_list_t controls; + for(child_list_t::const_iterator it = getChildList()->begin(), end_it = getChildList()->end(); it != end_it; ++it) + { + LLView* viewp = *it; + if(viewp->isCtrl()) + { + controls.push_back(static_cast(viewp)); + } + } + return controls; +} + + +void LLPanel::draw() +{ + F32 alpha = getDrawContext().mAlpha; + + // draw background + if( mBgVisible ) + { + alpha = getCurrentTransparency(); + + LLRect local_rect = getLocalRect(); + if (mBgOpaque ) + { + // opaque, in-front look + if (mBgOpaqueImage.notNull()) + { + mBgOpaqueImage->draw( local_rect, mBgOpaqueImageOverlay % alpha ); + } + else + { + // fallback to flat colors when there are no images + gl_rect_2d( local_rect, mBgOpaqueColor.get() % alpha); + } + } + else + { + // transparent, in-back look + if (mBgAlphaImage.notNull()) + { + mBgAlphaImage->draw( local_rect, mBgAlphaImageOverlay % alpha ); + } + else + { + gl_rect_2d( local_rect, mBgAlphaColor.get() % alpha ); + } + } + } + + updateDefaultBtn(); + + LLView::draw(); +} + +void LLPanel::updateDefaultBtn() +{ + if( mDefaultBtn) + { + if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled()) + { + LLButton* buttonp = dynamic_cast(gFocusMgr.getKeyboardFocus()); + bool focus_is_child_button = buttonp && buttonp->getCommitOnReturn(); + // only enable default button when current focus is not a return-capturing button + mDefaultBtn->setBorderEnabled(!focus_is_child_button); + } + else + { + mDefaultBtn->setBorderEnabled(false); + } + } +} + +void LLPanel::refresh() +{ + // do nothing by default + // but is automatically called in setFocus(true) +} + +void LLPanel::setDefaultBtn(LLButton* btn) +{ + if (mDefaultBtn && mDefaultBtn->getEnabled()) + { + mDefaultBtn->setBorderEnabled(false); + } + mDefaultBtn = btn; + if (mDefaultBtn) + { + mDefaultBtn->setBorderEnabled(true); + } +} + +void LLPanel::setDefaultBtn(const std::string& id) +{ + LLButton *button = getChild(id); + if (button) + { + setDefaultBtn(button); + } + else + { + setDefaultBtn(NULL); + } +} + +bool LLPanel::handleKeyHere( KEY key, MASK mask ) +{ + bool handled = false; + + LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + + // handle user hitting ESC to defocus + if (key == KEY_ESCAPE) + { + setFocus(false); + return true; + } + else if( (mask == MASK_SHIFT) && (KEY_TAB == key)) + { + //SHIFT-TAB + if (cur_focus) + { + LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); + if (focus_root) + { + handled = focus_root->focusPrevItem(false); + } + } + } + else if( (mask == MASK_NONE ) && (KEY_TAB == key)) + { + //TAB + if (cur_focus) + { + LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); + if (focus_root) + { + handled = focus_root->focusNextItem(false); + } + } + } + + // If RETURN was pressed and something has focus, call onCommit() + if (!handled && cur_focus && key == KEY_RETURN && mask == MASK_NONE) + { + LLButton* focused_button = dynamic_cast(cur_focus); + if (focused_button && focused_button->getCommitOnReturn()) + { + // current focus is a return-capturing button, + // let *that* button handle the return key + handled = false; + } + else if (mDefaultBtn && mDefaultBtn->getVisible() && mDefaultBtn->getEnabled()) + { + // If we have a default button, click it when return is pressed + mDefaultBtn->onCommit(); + handled = true; + } + else if (cur_focus->acceptsTextInput()) + { + // call onCommit for text input handling control + cur_focus->onCommit(); + handled = true; + } + } + + return handled; +} + +void LLPanel::onVisibilityChange ( bool new_visibility ) +{ + LLUICtrl::onVisibilityChange ( new_visibility ); + if (mVisibleSignal) + (*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass bool as LLSD +} + +void LLPanel::setFocus(bool b) +{ + if( b && !hasFocus()) + { + // give ourselves focus preemptively, to avoid infinite loop + LLUICtrl::setFocus(true); + // then try to pass to first valid child + focusFirstItem(); + } + else + { + LLUICtrl::setFocus(b); + } +} + +void LLPanel::setBorderVisible(bool b) +{ + if (mBorder) + { + mBorder->setVisible( b ); + } +} + +LLTrace::BlockTimerStatHandle FTM_PANEL_CONSTRUCTION("Panel Construction"); + +LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_node) +{ + std::string name("panel"); + node->getAttributeString("name", name); + + std::string class_attr; + node->getAttributeString("class", class_attr); + + LLPanel* panelp = NULL; + + { LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION); + + if(!class_attr.empty()) + { + panelp = LLRegisterPanelClass::instance().createPanelClass(class_attr); + if (!panelp) + { + LL_WARNS() << "Panel class \"" << class_attr << "\" not registered." << LL_ENDL; + } + } + + if (!panelp) + { + panelp = createFactoryPanel(name); + llassert(panelp); + + if (!panelp) + { + return NULL; // :( + } + } + + } + // factory panels may have registered their own factory maps + if (!panelp->getFactoryMap().empty()) + { + sFactoryStack.push_back(&panelp->getFactoryMap()); + } + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->mCommitCallbackRegistrar.pushScope(); + panelp->mEnableCallbackRegistrar.pushScope(); + + panelp->initPanelXML(node, parent, output_node, LLUICtrlFactory::getDefaultParams()); + + panelp->mCommitCallbackRegistrar.popScope(); + panelp->mEnableCallbackRegistrar.popScope(); + + if (!panelp->getFactoryMap().empty()) + { + sFactoryStack.pop_back(); + } + + return panelp; +} + +void LLPanel::initFromParams(const LLPanel::Params& p) +{ + //setting these here since panel constructor not called with params + //and LLView::initFromParams will use them to set visible and enabled + setVisible(p.visible); + setEnabled(p.enabled); + setFocusRoot(p.focus_root); + setSoundFlags(p.sound_flags); + + // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible + LLUICtrl::initFromParams(p); + + // visible callback + if (p.visible_callback.isProvided()) + { + setVisibleCallback(initCommitCallback(p.visible_callback)); + } + + for (LLInitParam::ParamIterator::const_iterator it = p.strings.begin(); + it != p.strings.end(); + ++it) + { + mUIStrings[it->name] = it->value; + } + + setLabel(p.label()); + setHelpTopic(p.help_topic); + setShape(p.rect); + parseFollowsFlags(p); + + setToolTip(p.tool_tip()); + setFromXUI(p.from_xui); + + mHoverCursor = getCursorFromString(p.hover_cursor); + + if (p.has_border) + { + addBorder(p.border); + } + // let constructors set this value if not provided + if (p.use_bounding_rect.isProvided()) + { + setUseBoundingRect(p.use_bounding_rect); + } + setDefaultTabGroup(p.default_tab_group); + setMouseOpaque(p.mouse_opaque); + + setBackgroundVisible(p.background_visible); + setBackgroundOpaque(p.background_opaque); + setBackgroundColor(p.bg_opaque_color().get()); + setTransparentColor(p.bg_alpha_color().get()); + mBgOpaqueImage = p.bg_opaque_image(); + mBgAlphaImage = p.bg_alpha_image(); + mBgOpaqueImageOverlay = p.bg_opaque_image_overlay; + mBgAlphaImageOverlay = p.bg_alpha_image_overlay; + + setAcceptsBadge(p.accepts_badge); +} + +static LLTrace::BlockTimerStatHandle FTM_PANEL_SETUP("Panel Setup"); +static LLTrace::BlockTimerStatHandle FTM_EXTERNAL_PANEL_LOAD("Load Extern Panel Reference"); +static LLTrace::BlockTimerStatHandle FTM_PANEL_POSTBUILD("Panel PostBuild"); + +bool LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params) +{ + Params params(default_params); + { + LL_RECORD_BLOCK_TIME(FTM_PANEL_SETUP); + + LLXMLNodePtr referenced_xml; + std::string xml_filename = mXMLFilename; + + // if the panel didn't provide a filename, check the node + if (xml_filename.empty()) + { + node->getAttributeString("filename", xml_filename); + setXMLFilename(xml_filename); + } + + LLXUIParser parser; + + if (!xml_filename.empty()) + { + if (output_node) + { + //if we are exporting, we want to export the current xml + //not the referenced xml + parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName()); + Params output_params(params); + setupParamsForExport(output_params, parent); + output_node->setName(node->getName()->mString); + parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); + return true; + } + + LLUICtrlFactory::instance().pushFileName(xml_filename); + + LL_RECORD_BLOCK_TIME(FTM_EXTERNAL_PANEL_LOAD); + if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml)) + { + LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL; + + return false; + } + + parser.readXUI(referenced_xml, params, LLUICtrlFactory::getInstance()->getCurFileName()); + + // add children using dimensions from referenced xml for consistent layout + setShape(params.rect); + LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance()); + + LLUICtrlFactory::instance().popFileName(); + } + + // ask LLUICtrlFactory for filename, since xml_filename might be empty + parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName()); + + if (output_node) + { + Params output_params(params); + setupParamsForExport(output_params, parent); + output_node->setName(node->getName()->mString); + parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params); + } + + params.from_xui = true; + applyXUILayout(params, parent); + { + LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION); + initFromParams(params); + } + + // add children + LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node); + + // Connect to parent after children are built, because tab containers + // do a reshape() on their child panels, which requires that the children + // be built/added. JC + if (parent) + { + S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : parent->getLastTabGroup(); + parent->addChild(this, tab_group); + } + + { + LL_RECORD_BLOCK_TIME(FTM_PANEL_POSTBUILD); + postBuild(); + } + } + return true; +} + +bool LLPanel::hasString(const std::string& name) +{ + return mUIStrings.find(name) != mUIStrings.end(); +} + +std::string LLPanel::getString(const std::string& name, const LLStringUtil::format_map_t& args) const +{ + ui_string_map_t::const_iterator found_it = mUIStrings.find(name); + if (found_it != mUIStrings.end()) + { + // make a copy as format works in place + LLUIString formatted_string = LLUIString(found_it->second); + formatted_string.setArgList(args); + return formatted_string.getString(); + } + std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate + if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode")) + { + LL_ERRS() << err_str << LL_ENDL; + } + else + { + LL_WARNS() << err_str << LL_ENDL; + } + return LLStringUtil::null; +} + +std::string LLPanel::getString(const std::string& name) const +{ + ui_string_map_t::const_iterator found_it = mUIStrings.find(name); + if (found_it != mUIStrings.end()) + { + return found_it->second; + } + std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate + if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode")) + { + LL_ERRS() << err_str << LL_ENDL; + } + else + { + LL_WARNS() << err_str << LL_ENDL; + } + return LLStringUtil::null; +} + + +void LLPanel::childSetVisible(const std::string& id, bool visible) +{ + LLView* child = findChild(id); + if (child) + { + child->setVisible(visible); + } +} + +void LLPanel::childSetEnabled(const std::string& id, bool enabled) +{ + LLView* child = findChild(id); + if (child) + { + child->setEnabled(enabled); + } +} + +void LLPanel::childSetFocus(const std::string& id, bool focus) +{ + LLUICtrl* child = findChild(id); + if (child) + { + child->setFocus(focus); + } +} + +bool LLPanel::childHasFocus(const std::string& id) +{ + LLUICtrl* child = findChild(id); + if (child) + { + return child->hasFocus(); + } + else + { + return false; + } +} + +// *TODO: Deprecate; for backwards compatability only: +// Prefer getChild("foo")->setCommitCallback(boost:bind(...)), +// which takes a generic slot. Or use mCommitCallbackRegistrar.add() with +// a named callback and reference it in XML. +void LLPanel::childSetCommitCallback(const std::string& id, boost::function cb, void* data) +{ + LLUICtrl* child = findChild(id); + if (child) + { + child->setCommitCallback(boost::bind(cb, child, data)); + } +} + +void LLPanel::childSetColor(const std::string& id, const LLColor4& color) +{ + LLUICtrl* child = findChild(id); + if (child) + { + child->setColor(color); + } +} + +LLCtrlSelectionInterface* LLPanel::childGetSelectionInterface(const std::string& id) const +{ + LLUICtrl* child = findChild(id); + if (child) + { + return child->getSelectionInterface(); + } + return NULL; +} + +LLCtrlListInterface* LLPanel::childGetListInterface(const std::string& id) const +{ + LLUICtrl* child = findChild(id); + if (child) + { + return child->getListInterface(); + } + return NULL; +} + +LLCtrlScrollInterface* LLPanel::childGetScrollInterface(const std::string& id) const +{ + LLUICtrl* child = findChild(id); + if (child) + { + return child->getScrollInterface(); + } + return NULL; +} + +void LLPanel::childSetValue(const std::string& id, LLSD value) +{ + LLUICtrl* child = findChild(id); + if (child) + { + child->setValue(value); + } +} + +LLSD LLPanel::childGetValue(const std::string& id) const +{ + LLUICtrl* child = findChild(id); + if (child) + { + return child->getValue(); + } + // Not found => return undefined + return LLSD(); +} + +bool LLPanel::childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text) +{ + LLUICtrl* child = findChild(id); + if (child) + { + return child->setTextArg(key, text); + } + return false; +} + +bool LLPanel::childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text) +{ + LLView* child = findChild(id); + if (child) + { + return child->setLabelArg(key, text); + } + return false; +} + +void LLPanel::childSetAction(const std::string& id, const commit_signal_t::slot_type& function) +{ + LLButton* button = findChild(id); + if (button) + { + button->setClickedCallback(function); + } +} + +void LLPanel::childSetAction(const std::string& id, boost::function function, void* value) +{ + LLButton* button = findChild(id); + if (button) + { + button->setClickedCallback(boost::bind(function, value)); + } +} + +boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mVisibleSignal) + { + mVisibleSignal = new commit_signal_t(); + } + + return mVisibleSignal->connect(cb); +} + +//----------------------------------------------------------------------------- +// buildPanel() +//----------------------------------------------------------------------------- +bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) +{ + LL_PROFILE_ZONE_SCOPED; + bool didPost = false; + LLXMLNodePtr root; + + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + { + LL_WARNS() << "Couldn't parse panel from: " << filename << LL_ENDL; + return didPost; + } + + // root must be called panel + if( !root->hasName("panel" ) ) + { + LL_WARNS() << "Root node should be named panel in : " << filename << LL_ENDL; + return didPost; + } + + LL_DEBUGS() << "Building panel " << filename << LL_ENDL; + + LLUICtrlFactory::instance().pushFileName(filename); + { + if (!getFactoryMap().empty()) + { + sFactoryStack.push_back(&getFactoryMap()); + } + + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + getCommitCallbackRegistrar().pushScope(); + getEnableCallbackRegistrar().pushScope(); + + didPost = initPanelXML(root, NULL, NULL, default_params); + + getCommitCallbackRegistrar().popScope(); + getEnableCallbackRegistrar().popScope(); + + setXMLFilename(filename); + + if (!getFactoryMap().empty()) + { + sFactoryStack.pop_back(); + } + } + LLUICtrlFactory::instance().popFileName(); + return didPost; +} + +//----------------------------------------------------------------------------- +// createFactoryPanel() +//----------------------------------------------------------------------------- +LLPanel* LLPanel::createFactoryPanel(const std::string& name) +{ + std::deque::iterator itor; + for (itor = sFactoryStack.begin(); itor != sFactoryStack.end(); ++itor) + { + const LLCallbackMap::map_t* factory_map = *itor; + + // Look up this panel's name in the map. + LLCallbackMap::map_const_iter_t iter = factory_map->find( name ); + if (iter != factory_map->end()) + { + // Use the factory to create the panel, instead of using a default LLPanel. + LLPanel *ret = (LLPanel*) iter->second.mCallback( iter->second.mData ); + return ret; + } + } + LLPanel::Params panel_p; + return LLUICtrlFactory::create(panel_p); +} diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 1088af0e04..2be5573faf 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -1,318 +1,318 @@ -/** - * @file llpanel.h - * @author James Cook, Tom Yedwab - * @brief LLPanel base class - * - * $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$ - */ - -#ifndef LL_LLPANEL_H -#define LL_LLPANEL_H - - -#include "llcallbackmap.h" -#include "lluictrl.h" -#include "llviewborder.h" -#include "lluiimage.h" -#include "lluistring.h" -#include "v4color.h" -#include "llbadgeholder.h" -#include -#include - -const S32 LLPANEL_BORDER_WIDTH = 1; -const bool BORDER_YES = true; -const bool BORDER_NO = false; - -class LLButton; -class LLUIImage; - -/* - * General purpose concrete view base class. - * Transparent or opaque, - * With or without border, - * Can contain LLUICtrls. - */ -class LLPanel : public LLUICtrl, public LLBadgeHolder -{ -public: - struct LocalizedString : public LLInitParam::Block - { - Mandatory name; - Mandatory value; - - LocalizedString(); - }; - - struct Params - : public LLInitParam::Block - { - Optional has_border; - Optional border; - - Optional background_visible, - background_opaque; - - Optional bg_opaque_color, - bg_alpha_color, - bg_opaque_image_overlay, - bg_alpha_image_overlay; - // opaque image is for "panel in foreground" look - Optional bg_opaque_image, - bg_alpha_image; - - Optional min_width, - min_height; - - Optional filename; - Optional class_name; - Optional help_topic; - - Multiple strings; - - Optional visible_callback; - - Optional accepts_badge; - - Params(); - }; - -protected: - friend class LLUICtrlFactory; - // RN: for some reason you can't just use LLUICtrlFactory::getDefaultParams as a default argument in VC8 - static const LLPanel::Params& getDefaultParams(); - - // Panels can get constructed directly - LLPanel(const LLPanel::Params& params = getDefaultParams()); - -public: - typedef std::vector ctrl_list_t; - - bool buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); - - static LLPanel* createFactoryPanel(const std::string& name); - - /*virtual*/ ~LLPanel(); - - // LLView interface - /*virtual*/ bool isPanel() const; - /*virtual*/ void draw(); - /*virtual*/ bool handleKeyHere( KEY key, MASK mask ); - /*virtual*/ void onVisibilityChange ( bool new_visibility ); - - // From LLFocusableElement - /*virtual*/ void setFocus( bool b ); - - // New virtuals - virtual void refresh(); // called in setFocus() - virtual void clearCtrls(); // overridden in LLPanelObject and LLPanelVolume - - // Border controls - const LLViewBorder* getBorder() const { return mBorder; } - void addBorder( LLViewBorder::Params p); - void addBorder(); - void removeBorder(); - bool hasBorder() const { return mBorder != NULL; } - void setBorderVisible( bool b ); - - void setBackgroundColor( const LLColor4& color ) { mBgOpaqueColor = color; } - const LLColor4& getBackgroundColor() const { return mBgOpaqueColor; } - void setTransparentColor(const LLColor4& color) { mBgAlphaColor = color; } - const LLColor4& getTransparentColor() const { return mBgAlphaColor; } - void setBackgroundImage(LLUIImage* image) { mBgOpaqueImage = image; } - void setTransparentImage(LLUIImage* image) { mBgAlphaImage = image; } - LLPointer getBackgroundImage() const { return mBgOpaqueImage; } - LLPointer getTransparentImage() const { return mBgAlphaImage; } - LLColor4 getBackgroundImageOverlay() { return mBgOpaqueImageOverlay; } - LLColor4 getTransparentImageOverlay() { return mBgAlphaImageOverlay; } - void setBackgroundVisible( bool b ) { mBgVisible = b; } - bool isBackgroundVisible() const { return mBgVisible; } - void setBackgroundOpaque(bool b) { mBgOpaque = b; } - bool isBackgroundOpaque() const { return mBgOpaque; } - void setDefaultBtn(LLButton* btn = NULL); - void setDefaultBtn(const std::string& id); - void updateDefaultBtn(); - void setLabel(const LLStringExplicit& label) { mLabel = label; } - std::string getLabel() const { return mLabel; } - void setHelpTopic(const std::string& help_topic) { mHelpTopic = help_topic; } - std::string getHelpTopic() const { return mHelpTopic; } - - void setCtrlsEnabled(bool b); - ctrl_list_t getCtrlList() const; - - LLHandle getHandle() const { return getDerivedHandle(); } - - const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; } - - CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; } - EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; } - - void initFromParams(const Params& p); - bool initPanelXML( LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params); - - bool hasString(const std::string& name); - std::string getString(const std::string& name, const LLStringUtil::format_map_t& args) const; - std::string getString(const std::string& name) const; - - // ** Wrappers for setting child properties by name ** -TomY - // WARNING: These are deprecated, please use getChild("name")->doStuff() idiom instead - - // LLView - void childSetVisible(const std::string& name, bool visible); - - void childSetEnabled(const std::string& name, bool enabled); - void childEnable(const std::string& name) { childSetEnabled(name, true); } - void childDisable(const std::string& name) { childSetEnabled(name, false); }; - - // LLUICtrl - void childSetFocus(const std::string& id, bool focus = true); - bool childHasFocus(const std::string& id); - - // *TODO: Deprecate; for backwards compatability only: - // Prefer getChild("foo")->setCommitCallback(boost:bind(...)), - // which takes a generic slot. Or use mCommitCallbackRegistrar.add() with - // a named callback and reference it in XML. - void childSetCommitCallback(const std::string& id, boost::function cb, void* data); - void childSetColor(const std::string& id, const LLColor4& color); - - LLCtrlSelectionInterface* childGetSelectionInterface(const std::string& id) const; - LLCtrlListInterface* childGetListInterface(const std::string& id) const; - LLCtrlScrollInterface* childGetScrollInterface(const std::string& id) const; - - // This is the magic bullet for data-driven UI - void childSetValue(const std::string& id, LLSD value); - LLSD childGetValue(const std::string& id) const; - - // For setting text / label replacement params, e.g. "Hello [NAME]" - // Not implemented for all types, defaults to noop, returns false if not applicaple - bool childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text); - bool childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text); - - // LLButton - void childSetAction(const std::string& id, boost::function function, void* value); - void childSetAction(const std::string& id, const commit_signal_t::slot_type& function); - - static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL); - - //call onOpen to let panel know when it's about to be shown or activated - virtual void onOpen(const LLSD& key) {} - - void setXMLFilename(std::string filename) { mXMLFilename = filename; }; - std::string getXMLFilename() { return mXMLFilename; }; - - boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb ); - -protected: - // Override to set not found list - LLButton* getDefaultButton() { return mDefaultBtn; } - LLCallbackMap::map_t mFactoryMap; - CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar; - EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar; - - commit_signal_t* mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD() - - std::string mHelpTopic; // the name of this panel's help topic to display in the Help Viewer - typedef std::deque factory_stack_t; - static factory_stack_t sFactoryStack; - - // for setting the xml filename when building panel in context dependent cases - std::string mXMLFilename; - -private: - bool mBgVisible; // any background at all? - bool mBgOpaque; // use opaque color or image - LLUIColor mBgOpaqueColor; - LLUIColor mBgAlphaColor; - LLUIColor mBgOpaqueImageOverlay; - LLUIColor mBgAlphaImageOverlay; - LLPointer mBgOpaqueImage; // "panel in front" look - LLPointer mBgAlphaImage; // "panel in back" look - LLViewBorder* mBorder; - LLButton* mDefaultBtn; - LLUIString mLabel; - - typedef std::map ui_string_map_t; - ui_string_map_t mUIStrings; - - -}; // end class LLPanel - -// Build time optimization, generate once in .cpp file -#ifndef LLPANEL_CPP -extern template class LLPanel* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -typedef boost::function LLPanelClassCreatorFunc; - -// local static instance for registering a particular panel class - -class LLRegisterPanelClass -: public LLSingleton< LLRegisterPanelClass > -{ - LLSINGLETON_EMPTY_CTOR(LLRegisterPanelClass); -public: - // register with either the provided builder, or the generic templated builder - void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func) - { - mPanelClassesNames[tag] = func; - } - - LLPanel* createPanelClass(const std::string& tag) - { - param_name_map_t::iterator iT = mPanelClassesNames.find(tag); - if(iT == mPanelClassesNames.end()) - return 0; - return iT->second(); - } - template - static T* defaultPanelClassBuilder() - { - T* pT = new T(); - return pT; - } - -private: - typedef std::map< std::string, LLPanelClassCreatorFunc> param_name_map_t; - - param_name_map_t mPanelClassesNames; -}; - - -// local static instance for registering a particular panel class -template - class LLPanelInjector -{ -public: - // register with either the provided builder, or the generic templated builder - LLPanelInjector(const std::string& tag); -}; - - -template - LLPanelInjector::LLPanelInjector(const std::string& tag) -{ - LLRegisterPanelClass::instance().addPanelClass(tag,&LLRegisterPanelClass::defaultPanelClassBuilder); -} - - -#endif +/** + * @file llpanel.h + * @author James Cook, Tom Yedwab + * @brief LLPanel base class + * + * $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$ + */ + +#ifndef LL_LLPANEL_H +#define LL_LLPANEL_H + + +#include "llcallbackmap.h" +#include "lluictrl.h" +#include "llviewborder.h" +#include "lluiimage.h" +#include "lluistring.h" +#include "v4color.h" +#include "llbadgeholder.h" +#include +#include + +const S32 LLPANEL_BORDER_WIDTH = 1; +const bool BORDER_YES = true; +const bool BORDER_NO = false; + +class LLButton; +class LLUIImage; + +/* + * General purpose concrete view base class. + * Transparent or opaque, + * With or without border, + * Can contain LLUICtrls. + */ +class LLPanel : public LLUICtrl, public LLBadgeHolder +{ +public: + struct LocalizedString : public LLInitParam::Block + { + Mandatory name; + Mandatory value; + + LocalizedString(); + }; + + struct Params + : public LLInitParam::Block + { + Optional has_border; + Optional border; + + Optional background_visible, + background_opaque; + + Optional bg_opaque_color, + bg_alpha_color, + bg_opaque_image_overlay, + bg_alpha_image_overlay; + // opaque image is for "panel in foreground" look + Optional bg_opaque_image, + bg_alpha_image; + + Optional min_width, + min_height; + + Optional filename; + Optional class_name; + Optional help_topic; + + Multiple strings; + + Optional visible_callback; + + Optional accepts_badge; + + Params(); + }; + +protected: + friend class LLUICtrlFactory; + // RN: for some reason you can't just use LLUICtrlFactory::getDefaultParams as a default argument in VC8 + static const LLPanel::Params& getDefaultParams(); + + // Panels can get constructed directly + LLPanel(const LLPanel::Params& params = getDefaultParams()); + +public: + typedef std::vector ctrl_list_t; + + bool buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); + + static LLPanel* createFactoryPanel(const std::string& name); + + /*virtual*/ ~LLPanel(); + + // LLView interface + /*virtual*/ bool isPanel() const; + /*virtual*/ void draw(); + /*virtual*/ bool handleKeyHere( KEY key, MASK mask ); + /*virtual*/ void onVisibilityChange ( bool new_visibility ); + + // From LLFocusableElement + /*virtual*/ void setFocus( bool b ); + + // New virtuals + virtual void refresh(); // called in setFocus() + virtual void clearCtrls(); // overridden in LLPanelObject and LLPanelVolume + + // Border controls + const LLViewBorder* getBorder() const { return mBorder; } + void addBorder( LLViewBorder::Params p); + void addBorder(); + void removeBorder(); + bool hasBorder() const { return mBorder != NULL; } + void setBorderVisible( bool b ); + + void setBackgroundColor( const LLColor4& color ) { mBgOpaqueColor = color; } + const LLColor4& getBackgroundColor() const { return mBgOpaqueColor; } + void setTransparentColor(const LLColor4& color) { mBgAlphaColor = color; } + const LLColor4& getTransparentColor() const { return mBgAlphaColor; } + void setBackgroundImage(LLUIImage* image) { mBgOpaqueImage = image; } + void setTransparentImage(LLUIImage* image) { mBgAlphaImage = image; } + LLPointer getBackgroundImage() const { return mBgOpaqueImage; } + LLPointer getTransparentImage() const { return mBgAlphaImage; } + LLColor4 getBackgroundImageOverlay() { return mBgOpaqueImageOverlay; } + LLColor4 getTransparentImageOverlay() { return mBgAlphaImageOverlay; } + void setBackgroundVisible( bool b ) { mBgVisible = b; } + bool isBackgroundVisible() const { return mBgVisible; } + void setBackgroundOpaque(bool b) { mBgOpaque = b; } + bool isBackgroundOpaque() const { return mBgOpaque; } + void setDefaultBtn(LLButton* btn = NULL); + void setDefaultBtn(const std::string& id); + void updateDefaultBtn(); + void setLabel(const LLStringExplicit& label) { mLabel = label; } + std::string getLabel() const { return mLabel; } + void setHelpTopic(const std::string& help_topic) { mHelpTopic = help_topic; } + std::string getHelpTopic() const { return mHelpTopic; } + + void setCtrlsEnabled(bool b); + ctrl_list_t getCtrlList() const; + + LLHandle getHandle() const { return getDerivedHandle(); } + + const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; } + + CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; } + EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; } + + void initFromParams(const Params& p); + bool initPanelXML( LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params); + + bool hasString(const std::string& name); + std::string getString(const std::string& name, const LLStringUtil::format_map_t& args) const; + std::string getString(const std::string& name) const; + + // ** Wrappers for setting child properties by name ** -TomY + // WARNING: These are deprecated, please use getChild("name")->doStuff() idiom instead + + // LLView + void childSetVisible(const std::string& name, bool visible); + + void childSetEnabled(const std::string& name, bool enabled); + void childEnable(const std::string& name) { childSetEnabled(name, true); } + void childDisable(const std::string& name) { childSetEnabled(name, false); }; + + // LLUICtrl + void childSetFocus(const std::string& id, bool focus = true); + bool childHasFocus(const std::string& id); + + // *TODO: Deprecate; for backwards compatability only: + // Prefer getChild("foo")->setCommitCallback(boost:bind(...)), + // which takes a generic slot. Or use mCommitCallbackRegistrar.add() with + // a named callback and reference it in XML. + void childSetCommitCallback(const std::string& id, boost::function cb, void* data); + void childSetColor(const std::string& id, const LLColor4& color); + + LLCtrlSelectionInterface* childGetSelectionInterface(const std::string& id) const; + LLCtrlListInterface* childGetListInterface(const std::string& id) const; + LLCtrlScrollInterface* childGetScrollInterface(const std::string& id) const; + + // This is the magic bullet for data-driven UI + void childSetValue(const std::string& id, LLSD value); + LLSD childGetValue(const std::string& id) const; + + // For setting text / label replacement params, e.g. "Hello [NAME]" + // Not implemented for all types, defaults to noop, returns false if not applicaple + bool childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text); + bool childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text); + + // LLButton + void childSetAction(const std::string& id, boost::function function, void* value); + void childSetAction(const std::string& id, const commit_signal_t::slot_type& function); + + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL); + + //call onOpen to let panel know when it's about to be shown or activated + virtual void onOpen(const LLSD& key) {} + + void setXMLFilename(std::string filename) { mXMLFilename = filename; }; + std::string getXMLFilename() { return mXMLFilename; }; + + boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb ); + +protected: + // Override to set not found list + LLButton* getDefaultButton() { return mDefaultBtn; } + LLCallbackMap::map_t mFactoryMap; + CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar; + EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar; + + commit_signal_t* mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD() + + std::string mHelpTopic; // the name of this panel's help topic to display in the Help Viewer + typedef std::deque factory_stack_t; + static factory_stack_t sFactoryStack; + + // for setting the xml filename when building panel in context dependent cases + std::string mXMLFilename; + +private: + bool mBgVisible; // any background at all? + bool mBgOpaque; // use opaque color or image + LLUIColor mBgOpaqueColor; + LLUIColor mBgAlphaColor; + LLUIColor mBgOpaqueImageOverlay; + LLUIColor mBgAlphaImageOverlay; + LLPointer mBgOpaqueImage; // "panel in front" look + LLPointer mBgAlphaImage; // "panel in back" look + LLViewBorder* mBorder; + LLButton* mDefaultBtn; + LLUIString mLabel; + + typedef std::map ui_string_map_t; + ui_string_map_t mUIStrings; + + +}; // end class LLPanel + +// Build time optimization, generate once in .cpp file +#ifndef LLPANEL_CPP +extern template class LLPanel* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +typedef boost::function LLPanelClassCreatorFunc; + +// local static instance for registering a particular panel class + +class LLRegisterPanelClass +: public LLSingleton< LLRegisterPanelClass > +{ + LLSINGLETON_EMPTY_CTOR(LLRegisterPanelClass); +public: + // register with either the provided builder, or the generic templated builder + void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func) + { + mPanelClassesNames[tag] = func; + } + + LLPanel* createPanelClass(const std::string& tag) + { + param_name_map_t::iterator iT = mPanelClassesNames.find(tag); + if(iT == mPanelClassesNames.end()) + return 0; + return iT->second(); + } + template + static T* defaultPanelClassBuilder() + { + T* pT = new T(); + return pT; + } + +private: + typedef std::map< std::string, LLPanelClassCreatorFunc> param_name_map_t; + + param_name_map_t mPanelClassesNames; +}; + + +// local static instance for registering a particular panel class +template + class LLPanelInjector +{ +public: + // register with either the provided builder, or the generic templated builder + LLPanelInjector(const std::string& tag); +}; + + +template + LLPanelInjector::LLPanelInjector(const std::string& tag) +{ + LLRegisterPanelClass::instance().addPanelClass(tag,&LLRegisterPanelClass::defaultPanelClassBuilder); +} + + +#endif diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index e542dac019..2aff434612 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -1,521 +1,521 @@ -/** - * @file llradiogroup.cpp - * @brief LLRadioGroup base class - * - * $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 "linden_common.h" - -#include "llboost.h" - -#include "llradiogroup.h" -#include "indra_constants.h" - -#include "llviewborder.h" -#include "llcontrol.h" -#include "llui.h" -#include "llfocusmgr.h" -#include "lluictrlfactory.h" -#include "llsdutil.h" - -static LLDefaultChildRegistry::Register r1("radio_group"); - -/* - * An invisible view containing multiple mutually exclusive toggling - * buttons (usually radio buttons). Automatically handles the mutex - * condition by highlighting only one button at a time. - */ -class LLRadioCtrl : public LLCheckBoxCtrl -{ -public: - typedef LLRadioGroup::ItemParams Params; - /*virtual*/ ~LLRadioCtrl(); - /*virtual*/ void setValue(const LLSD& value); - - /*virtual*/ bool postBuild(); - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - - LLSD getPayload() { return mPayload; } - - // Ensure label is in an attribute, not the contents - static void setupParamsForExport(Params& p, LLView* parent); - -protected: - LLRadioCtrl(const LLRadioGroup::ItemParams& p); - friend class LLUICtrlFactory; - - LLSD mPayload; // stores data that this item represents in the radio group -}; -static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item"); - -LLRadioGroup::Params::Params() -: allow_deselect("allow_deselect"), - items("item") -{ - addSynonym(items, "radio_item"); - - // radio items are not tabbable until they are selected - tab_stop = false; -} - -LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p) -: LLUICtrl(p), - mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), - mSelectedIndex(-1), - mAllowDeselect(p.allow_deselect) -{} - -void LLRadioGroup::initFromParams(const Params& p) -{ - for (LLInitParam::ParamIterator::const_iterator it = p.items.begin(); - it != p.items.end(); - ++it) - { - LLRadioGroup::ItemParams item_params(*it); - - if (!item_params.font.isProvided()) - { - item_params.font = mFont; // apply radio group font by default - } - item_params.commit_callback.function = boost::bind(&LLRadioGroup::onClickButton, this, _1); - item_params.from_xui = p.from_xui; - if (p.from_xui) - { - applyXUILayout(item_params, this); - } - - LLRadioCtrl* item = LLUICtrlFactory::create(item_params, this); - mRadioButtons.push_back(item); - } - - // call this *after* setting up mRadioButtons so we can handle setValue() calls - LLUICtrl::initFromParams(p); -} - - -LLRadioGroup::~LLRadioGroup() -{ -} - -// virtual -bool LLRadioGroup::postBuild() -{ - if (!mRadioButtons.empty()) - { - mRadioButtons[0]->setTabStop(true); - } - return true; -} - -void LLRadioGroup::setIndexEnabled(S32 index, bool enabled) -{ - S32 count = 0; - for (button_list_t::iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - LLRadioCtrl* child = *iter; - if (count == index) - { - child->setEnabled(enabled); - if (index == mSelectedIndex && !enabled) - { - setSelectedIndex(-1); - } - break; - } - count++; - } - count = 0; - if (mSelectedIndex < 0) - { - // Set to highest enabled value < index, - // or lowest value above index if none lower are enabled - // or 0 if none are enabled - for (button_list_t::iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - LLRadioCtrl* child = *iter; - if (count >= index && mSelectedIndex >= 0) - { - break; - } - if (child->getEnabled()) - { - setSelectedIndex(count); - } - count++; - } - if (mSelectedIndex < 0) - { - setSelectedIndex(0); - } - } -} - -bool LLRadioGroup::setSelectedIndex(S32 index, bool from_event) -{ - if ((S32)mRadioButtons.size() <= index ) - { - return false; - } - - if (index < -1) - { - // less then minimum value - return false; - } - - if (index < 0 && mSelectedIndex >= 0 && !mAllowDeselect) - { - // -1 is "nothing selected" - return false; - } - - if (mSelectedIndex >= 0) - { - LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex]; - old_radio_item->setTabStop(false); - old_radio_item->setValue( false ); - } - else - { - mRadioButtons[0]->setTabStop(false); - } - - mSelectedIndex = index; - - if (mSelectedIndex >= 0) - { - LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; - radio_item->setTabStop(true); - radio_item->setValue( true ); - - if (hasFocus()) - { - radio_item->focusFirstItem(false, false); - } - } - - if (!from_event) - { - setControlValue(getValue()); - } - - return true; -} - -void LLRadioGroup::focusSelectedRadioBtn() -{ - if (mSelectedIndex >= 0) - { - LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; - if (radio_item->hasTabStop() && radio_item->getEnabled()) - { - radio_item->focusFirstItem(false, false); - } - } - else if (mRadioButtons[0]->hasTabStop() || hasTabStop()) - { - focusFirstItem(false, false); - } -} - -bool LLRadioGroup::handleKeyHere(KEY key, MASK mask) -{ - bool handled = false; - // do any of the tab buttons have keyboard focus? - if (mask == MASK_NONE) - { - switch(key) - { - case KEY_DOWN: - if (!setSelectedIndex((getSelectedIndex() + 1))) - { - make_ui_sound("UISndInvalidOp"); - } - else - { - onCommit(); - } - handled = true; - break; - case KEY_UP: - if (!setSelectedIndex((getSelectedIndex() - 1))) - { - make_ui_sound("UISndInvalidOp"); - } - else - { - onCommit(); - } - handled = true; - break; - case KEY_LEFT: - if (!setSelectedIndex((getSelectedIndex() - 1))) - { - make_ui_sound("UISndInvalidOp"); - } - else - { - onCommit(); - } - handled = true; - break; - case KEY_RIGHT: - if (!setSelectedIndex((getSelectedIndex() + 1))) - { - make_ui_sound("UISndInvalidOp"); - } - else - { - onCommit(); - } - handled = true; - break; - default: - break; - } - } - return handled; -} - -// Handle one button being clicked. All child buttons must have this -// function as their callback function. - -void LLRadioGroup::onClickButton(LLUICtrl* ctrl) -{ - // LL_INFOS() << "LLRadioGroup::onClickButton" << LL_ENDL; - LLRadioCtrl* clicked_radio = dynamic_cast(ctrl); - if (!clicked_radio) - return; - S32 index = 0; - for (button_list_t::iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - LLRadioCtrl* radio = *iter; - if (radio == clicked_radio) - { - if (index == mSelectedIndex && mAllowDeselect) - { - // don't select anything - setSelectedIndex(-1); - } - else - { - setSelectedIndex(index); - } - - // BUG: Calls click callback even if button didn't actually change - onCommit(); - - return; - } - - index++; - } - - LL_WARNS() << "LLRadioGroup::onClickButton - clicked button that isn't a child" << LL_ENDL; -} - -void LLRadioGroup::setValue( const LLSD& value ) -{ - int idx = 0; - for (button_list_t::const_iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - LLRadioCtrl* radio = *iter; - if (radio->getPayload().asString() == value.asString()) - { - setSelectedIndex(idx); - idx = -1; - break; - } - ++idx; - } - if (idx != -1) - { - // string not found, try integer - if (value.isInteger()) - { - setSelectedIndex((S32) value.asInteger(), true); - } - else - { - setSelectedIndex(-1, true); - } - } -} - -LLSD LLRadioGroup::getValue() const -{ - int index = getSelectedIndex(); - int idx = 0; - for (button_list_t::const_iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - if (idx == index) return LLSD((*iter)->getPayload()); - ++idx; - } - return LLSD(); -} - -// LLCtrlSelectionInterface functions -bool LLRadioGroup::setCurrentByID( const LLUUID& id ) -{ - return false; -} - -LLUUID LLRadioGroup::getCurrentID() const -{ - return LLUUID::null; -} - -bool LLRadioGroup::setSelectedByValue(const LLSD& value, bool selected) -{ - S32 idx = 0; - for (button_list_t::const_iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - if((*iter)->getPayload().asString() == value.asString()) - { - setSelectedIndex(idx); - return true; - } - idx++; - } - - return false; -} - -LLSD LLRadioGroup::getSelectedValue() -{ - return getValue(); -} - -bool LLRadioGroup::isSelected(const LLSD& value) const -{ - S32 idx = 0; - for (button_list_t::const_iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - if((*iter)->getPayload().asString() == value.asString()) - { - if (idx == mSelectedIndex) - { - return true; - } - } - idx++; - } - return false; -} - -bool LLRadioGroup::operateOnSelection(EOperation op) -{ - return false; -} - -bool LLRadioGroup::operateOnAll(EOperation op) -{ - return false; -} - -LLRadioGroup::ItemParams::ItemParams() -: value("value") -{ - addSynonym(value, "initial_value"); -} - -LLRadioCtrl::LLRadioCtrl(const LLRadioGroup::ItemParams& p) -: LLCheckBoxCtrl(p), - mPayload(p.value) -{ - // use name as default "Value" for backwards compatibility - if (!p.value.isProvided()) - { - mPayload = p.name(); - } -} - -bool LLRadioCtrl::postBuild() -{ - // Old-style radio_item used the text contents to indicate the label, - // but new-style radio_item uses label attribute. - std::string value = getValue().asString(); - if (!value.empty()) - { - setLabel(value); - } - return true; -} - -bool LLRadioCtrl::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // Grab focus preemptively, before button takes mousecapture - if (hasTabStop() && getEnabled()) - { - focusFirstItem(false, false); - } - else - { - // Only currently selected item in group has tab stop as result it is - // unclear how focus should behave on click, just let the group handle - // focus and LLRadioGroup::onClickButton() will set correct state later - // if needed - LLRadioGroup* parent = (LLRadioGroup*)getParent(); - if (parent) - { - parent->focusSelectedRadioBtn(); - } - } - - return LLCheckBoxCtrl::handleMouseDown(x, y, mask); -} - -LLRadioCtrl::~LLRadioCtrl() -{ -} - -void LLRadioCtrl::setValue(const LLSD& value) -{ - LLCheckBoxCtrl::setValue(value); - mButton->setTabStop(value.asBoolean()); -} - -// *TODO: Remove this function after the initial XUI XML re-export pass. -// static -void LLRadioCtrl::setupParamsForExport(Params& p, LLView* parent) -{ - std::string label = p.label; - if (label.empty()) - { - // We don't have a label attribute, so move the text contents - // stored in "value" into the label - std::string initial_value = p.LLUICtrl::Params::initial_value(); - p.label = initial_value; - p.LLUICtrl::Params::initial_value = LLSD(); - } - - LLCheckBoxCtrl::setupParamsForExport(p, parent); -} +/** + * @file llradiogroup.cpp + * @brief LLRadioGroup base class + * + * $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 "linden_common.h" + +#include "llboost.h" + +#include "llradiogroup.h" +#include "indra_constants.h" + +#include "llviewborder.h" +#include "llcontrol.h" +#include "llui.h" +#include "llfocusmgr.h" +#include "lluictrlfactory.h" +#include "llsdutil.h" + +static LLDefaultChildRegistry::Register r1("radio_group"); + +/* + * An invisible view containing multiple mutually exclusive toggling + * buttons (usually radio buttons). Automatically handles the mutex + * condition by highlighting only one button at a time. + */ +class LLRadioCtrl : public LLCheckBoxCtrl +{ +public: + typedef LLRadioGroup::ItemParams Params; + /*virtual*/ ~LLRadioCtrl(); + /*virtual*/ void setValue(const LLSD& value); + + /*virtual*/ bool postBuild(); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + + LLSD getPayload() { return mPayload; } + + // Ensure label is in an attribute, not the contents + static void setupParamsForExport(Params& p, LLView* parent); + +protected: + LLRadioCtrl(const LLRadioGroup::ItemParams& p); + friend class LLUICtrlFactory; + + LLSD mPayload; // stores data that this item represents in the radio group +}; +static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item"); + +LLRadioGroup::Params::Params() +: allow_deselect("allow_deselect"), + items("item") +{ + addSynonym(items, "radio_item"); + + // radio items are not tabbable until they are selected + tab_stop = false; +} + +LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p) +: LLUICtrl(p), + mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), + mSelectedIndex(-1), + mAllowDeselect(p.allow_deselect) +{} + +void LLRadioGroup::initFromParams(const Params& p) +{ + for (LLInitParam::ParamIterator::const_iterator it = p.items.begin(); + it != p.items.end(); + ++it) + { + LLRadioGroup::ItemParams item_params(*it); + + if (!item_params.font.isProvided()) + { + item_params.font = mFont; // apply radio group font by default + } + item_params.commit_callback.function = boost::bind(&LLRadioGroup::onClickButton, this, _1); + item_params.from_xui = p.from_xui; + if (p.from_xui) + { + applyXUILayout(item_params, this); + } + + LLRadioCtrl* item = LLUICtrlFactory::create(item_params, this); + mRadioButtons.push_back(item); + } + + // call this *after* setting up mRadioButtons so we can handle setValue() calls + LLUICtrl::initFromParams(p); +} + + +LLRadioGroup::~LLRadioGroup() +{ +} + +// virtual +bool LLRadioGroup::postBuild() +{ + if (!mRadioButtons.empty()) + { + mRadioButtons[0]->setTabStop(true); + } + return true; +} + +void LLRadioGroup::setIndexEnabled(S32 index, bool enabled) +{ + S32 count = 0; + for (button_list_t::iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* child = *iter; + if (count == index) + { + child->setEnabled(enabled); + if (index == mSelectedIndex && !enabled) + { + setSelectedIndex(-1); + } + break; + } + count++; + } + count = 0; + if (mSelectedIndex < 0) + { + // Set to highest enabled value < index, + // or lowest value above index if none lower are enabled + // or 0 if none are enabled + for (button_list_t::iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* child = *iter; + if (count >= index && mSelectedIndex >= 0) + { + break; + } + if (child->getEnabled()) + { + setSelectedIndex(count); + } + count++; + } + if (mSelectedIndex < 0) + { + setSelectedIndex(0); + } + } +} + +bool LLRadioGroup::setSelectedIndex(S32 index, bool from_event) +{ + if ((S32)mRadioButtons.size() <= index ) + { + return false; + } + + if (index < -1) + { + // less then minimum value + return false; + } + + if (index < 0 && mSelectedIndex >= 0 && !mAllowDeselect) + { + // -1 is "nothing selected" + return false; + } + + if (mSelectedIndex >= 0) + { + LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex]; + old_radio_item->setTabStop(false); + old_radio_item->setValue( false ); + } + else + { + mRadioButtons[0]->setTabStop(false); + } + + mSelectedIndex = index; + + if (mSelectedIndex >= 0) + { + LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; + radio_item->setTabStop(true); + radio_item->setValue( true ); + + if (hasFocus()) + { + radio_item->focusFirstItem(false, false); + } + } + + if (!from_event) + { + setControlValue(getValue()); + } + + return true; +} + +void LLRadioGroup::focusSelectedRadioBtn() +{ + if (mSelectedIndex >= 0) + { + LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; + if (radio_item->hasTabStop() && radio_item->getEnabled()) + { + radio_item->focusFirstItem(false, false); + } + } + else if (mRadioButtons[0]->hasTabStop() || hasTabStop()) + { + focusFirstItem(false, false); + } +} + +bool LLRadioGroup::handleKeyHere(KEY key, MASK mask) +{ + bool handled = false; + // do any of the tab buttons have keyboard focus? + if (mask == MASK_NONE) + { + switch(key) + { + case KEY_DOWN: + if (!setSelectedIndex((getSelectedIndex() + 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = true; + break; + case KEY_UP: + if (!setSelectedIndex((getSelectedIndex() - 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = true; + break; + case KEY_LEFT: + if (!setSelectedIndex((getSelectedIndex() - 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = true; + break; + case KEY_RIGHT: + if (!setSelectedIndex((getSelectedIndex() + 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = true; + break; + default: + break; + } + } + return handled; +} + +// Handle one button being clicked. All child buttons must have this +// function as their callback function. + +void LLRadioGroup::onClickButton(LLUICtrl* ctrl) +{ + // LL_INFOS() << "LLRadioGroup::onClickButton" << LL_ENDL; + LLRadioCtrl* clicked_radio = dynamic_cast(ctrl); + if (!clicked_radio) + return; + S32 index = 0; + for (button_list_t::iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* radio = *iter; + if (radio == clicked_radio) + { + if (index == mSelectedIndex && mAllowDeselect) + { + // don't select anything + setSelectedIndex(-1); + } + else + { + setSelectedIndex(index); + } + + // BUG: Calls click callback even if button didn't actually change + onCommit(); + + return; + } + + index++; + } + + LL_WARNS() << "LLRadioGroup::onClickButton - clicked button that isn't a child" << LL_ENDL; +} + +void LLRadioGroup::setValue( const LLSD& value ) +{ + int idx = 0; + for (button_list_t::const_iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* radio = *iter; + if (radio->getPayload().asString() == value.asString()) + { + setSelectedIndex(idx); + idx = -1; + break; + } + ++idx; + } + if (idx != -1) + { + // string not found, try integer + if (value.isInteger()) + { + setSelectedIndex((S32) value.asInteger(), true); + } + else + { + setSelectedIndex(-1, true); + } + } +} + +LLSD LLRadioGroup::getValue() const +{ + int index = getSelectedIndex(); + int idx = 0; + for (button_list_t::const_iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + if (idx == index) return LLSD((*iter)->getPayload()); + ++idx; + } + return LLSD(); +} + +// LLCtrlSelectionInterface functions +bool LLRadioGroup::setCurrentByID( const LLUUID& id ) +{ + return false; +} + +LLUUID LLRadioGroup::getCurrentID() const +{ + return LLUUID::null; +} + +bool LLRadioGroup::setSelectedByValue(const LLSD& value, bool selected) +{ + S32 idx = 0; + for (button_list_t::const_iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + if((*iter)->getPayload().asString() == value.asString()) + { + setSelectedIndex(idx); + return true; + } + idx++; + } + + return false; +} + +LLSD LLRadioGroup::getSelectedValue() +{ + return getValue(); +} + +bool LLRadioGroup::isSelected(const LLSD& value) const +{ + S32 idx = 0; + for (button_list_t::const_iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + if((*iter)->getPayload().asString() == value.asString()) + { + if (idx == mSelectedIndex) + { + return true; + } + } + idx++; + } + return false; +} + +bool LLRadioGroup::operateOnSelection(EOperation op) +{ + return false; +} + +bool LLRadioGroup::operateOnAll(EOperation op) +{ + return false; +} + +LLRadioGroup::ItemParams::ItemParams() +: value("value") +{ + addSynonym(value, "initial_value"); +} + +LLRadioCtrl::LLRadioCtrl(const LLRadioGroup::ItemParams& p) +: LLCheckBoxCtrl(p), + mPayload(p.value) +{ + // use name as default "Value" for backwards compatibility + if (!p.value.isProvided()) + { + mPayload = p.name(); + } +} + +bool LLRadioCtrl::postBuild() +{ + // Old-style radio_item used the text contents to indicate the label, + // but new-style radio_item uses label attribute. + std::string value = getValue().asString(); + if (!value.empty()) + { + setLabel(value); + } + return true; +} + +bool LLRadioCtrl::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // Grab focus preemptively, before button takes mousecapture + if (hasTabStop() && getEnabled()) + { + focusFirstItem(false, false); + } + else + { + // Only currently selected item in group has tab stop as result it is + // unclear how focus should behave on click, just let the group handle + // focus and LLRadioGroup::onClickButton() will set correct state later + // if needed + LLRadioGroup* parent = (LLRadioGroup*)getParent(); + if (parent) + { + parent->focusSelectedRadioBtn(); + } + } + + return LLCheckBoxCtrl::handleMouseDown(x, y, mask); +} + +LLRadioCtrl::~LLRadioCtrl() +{ +} + +void LLRadioCtrl::setValue(const LLSD& value) +{ + LLCheckBoxCtrl::setValue(value); + mButton->setTabStop(value.asBoolean()); +} + +// *TODO: Remove this function after the initial XUI XML re-export pass. +// static +void LLRadioCtrl::setupParamsForExport(Params& p, LLView* parent) +{ + std::string label = p.label; + if (label.empty()) + { + // We don't have a label attribute, so move the text contents + // stored in "value" into the label + std::string initial_value = p.LLUICtrl::Params::initial_value(); + p.label = initial_value; + p.LLUICtrl::Params::initial_value = LLSD(); + } + + LLCheckBoxCtrl::setupParamsForExport(p, parent); +} diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index d72fd83668..810830ffa4 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -1,114 +1,114 @@ -/** - * @file llradiogroup.h - * @brief LLRadioGroup base class - * - * $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$ - */ - -#ifndef LL_LLRADIOGROUP_H -#define LL_LLRADIOGROUP_H - -#include "lluictrl.h" -#include "llcheckboxctrl.h" -#include "llctrlselectioninterface.h" - -/* - * An invisible view containing multiple mutually exclusive toggling - * buttons (usually radio buttons). Automatically handles the mutex - * condition by highlighting only one button at a time. - */ -class LLRadioGroup -: public LLUICtrl, public LLCtrlSelectionInterface -{ -public: - - struct ItemParams : public LLInitParam::Block - { - Optional value; - ItemParams(); - }; - - struct Params : public LLInitParam::Block - { - Optional allow_deselect; - Multiple > items; - Params(); - }; - -protected: - LLRadioGroup(const Params&); - friend class LLUICtrlFactory; - -public: - - /*virtual*/ void initFromParams(const Params&); - - virtual ~LLRadioGroup(); - - virtual bool postBuild(); - - virtual bool handleKeyHere(KEY key, MASK mask); - - void setIndexEnabled(S32 index, bool enabled); - // return the index value of the selected item - S32 getSelectedIndex() const { return mSelectedIndex; } - // set the index value programatically - bool setSelectedIndex(S32 index, bool from_event = false); - // foxus child by index if it can get focus - void focusSelectedRadioBtn(); - - // Accept and retrieve strings of the radio group control names - virtual void setValue(const LLSD& value ); - virtual LLSD getValue() const; - - // Update the control as needed. Userdata must be a pointer to the button. - void onClickButton(LLUICtrl* clicked_radio); - - //======================================================================== - LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }; - - // LLCtrlSelectionInterface functions - /*virtual*/ S32 getItemCount() const { return mRadioButtons.size(); } - /*virtual*/ bool getCanSelect() const { return true; } - /*virtual*/ bool selectFirstItem() { return setSelectedIndex(0); } - /*virtual*/ bool selectNthItem( S32 index ) { return setSelectedIndex(index); } - /*virtual*/ bool selectItemRange( S32 first, S32 last ) { return setSelectedIndex(first); } - /*virtual*/ S32 getFirstSelectedIndex() const { return getSelectedIndex(); } - /*virtual*/ bool setCurrentByID( const LLUUID& id ); - /*virtual*/ LLUUID getCurrentID() const; // LLUUID::null if no items in menu - /*virtual*/ bool setSelectedByValue(const LLSD& value, bool selected); - /*virtual*/ LLSD getSelectedValue(); - /*virtual*/ bool isSelected(const LLSD& value) const; - /*virtual*/ bool operateOnSelection(EOperation op); - /*virtual*/ bool operateOnAll(EOperation op); - -private: - const LLFontGL* mFont; - S32 mSelectedIndex; - - typedef std::vector button_list_t; - button_list_t mRadioButtons; - - bool mAllowDeselect; // user can click on an already selected option to deselect it -}; - -#endif +/** + * @file llradiogroup.h + * @brief LLRadioGroup base class + * + * $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$ + */ + +#ifndef LL_LLRADIOGROUP_H +#define LL_LLRADIOGROUP_H + +#include "lluictrl.h" +#include "llcheckboxctrl.h" +#include "llctrlselectioninterface.h" + +/* + * An invisible view containing multiple mutually exclusive toggling + * buttons (usually radio buttons). Automatically handles the mutex + * condition by highlighting only one button at a time. + */ +class LLRadioGroup +: public LLUICtrl, public LLCtrlSelectionInterface +{ +public: + + struct ItemParams : public LLInitParam::Block + { + Optional value; + ItemParams(); + }; + + struct Params : public LLInitParam::Block + { + Optional allow_deselect; + Multiple > items; + Params(); + }; + +protected: + LLRadioGroup(const Params&); + friend class LLUICtrlFactory; + +public: + + /*virtual*/ void initFromParams(const Params&); + + virtual ~LLRadioGroup(); + + virtual bool postBuild(); + + virtual bool handleKeyHere(KEY key, MASK mask); + + void setIndexEnabled(S32 index, bool enabled); + // return the index value of the selected item + S32 getSelectedIndex() const { return mSelectedIndex; } + // set the index value programatically + bool setSelectedIndex(S32 index, bool from_event = false); + // foxus child by index if it can get focus + void focusSelectedRadioBtn(); + + // Accept and retrieve strings of the radio group control names + virtual void setValue(const LLSD& value ); + virtual LLSD getValue() const; + + // Update the control as needed. Userdata must be a pointer to the button. + void onClickButton(LLUICtrl* clicked_radio); + + //======================================================================== + LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }; + + // LLCtrlSelectionInterface functions + /*virtual*/ S32 getItemCount() const { return mRadioButtons.size(); } + /*virtual*/ bool getCanSelect() const { return true; } + /*virtual*/ bool selectFirstItem() { return setSelectedIndex(0); } + /*virtual*/ bool selectNthItem( S32 index ) { return setSelectedIndex(index); } + /*virtual*/ bool selectItemRange( S32 first, S32 last ) { return setSelectedIndex(first); } + /*virtual*/ S32 getFirstSelectedIndex() const { return getSelectedIndex(); } + /*virtual*/ bool setCurrentByID( const LLUUID& id ); + /*virtual*/ LLUUID getCurrentID() const; // LLUUID::null if no items in menu + /*virtual*/ bool setSelectedByValue(const LLSD& value, bool selected); + /*virtual*/ LLSD getSelectedValue(); + /*virtual*/ bool isSelected(const LLSD& value) const; + /*virtual*/ bool operateOnSelection(EOperation op); + /*virtual*/ bool operateOnAll(EOperation op); + +private: + const LLFontGL* mFont; + S32 mSelectedIndex; + + typedef std::vector button_list_t; + button_list_t mRadioButtons; + + bool mAllowDeselect; // user can click on an already selected option to deselect it +}; + +#endif diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp index 85d886a3ed..a022fd2853 100644 --- a/indra/llui/llresizebar.cpp +++ b/indra/llui/llresizebar.cpp @@ -1,376 +1,376 @@ -/** - * @file llresizebar.cpp - * @brief LLResizeBar base class - * - * $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 "linden_common.h" - -#include "llresizebar.h" - -#include "lllocalcliprect.h" -#include "llmath.h" -#include "llui.h" -#include "llmenugl.h" -#include "llfocusmgr.h" -#include "llwindow.h" - -LLResizeBar::Params::Params() -: max_size("max_size", S32_MAX), - snapping_enabled("snapping_enabled", true), - resizing_view("resizing_view"), - side("side"), - allow_double_click_snapping("allow_double_click_snapping", true) -{ - name = "resize_bar"; -} - -LLResizeBar::LLResizeBar(const LLResizeBar::Params& p) -: LLView(p), - mDragLastScreenX( 0 ), - mDragLastScreenY( 0 ), - mLastMouseScreenX( 0 ), - mLastMouseScreenY( 0 ), - mMinSize( p.min_size ), - mMaxSize( p.max_size ), - mSide( p.side ), - mSnappingEnabled(p.snapping_enabled), - mAllowDoubleClickSnapping(p.allow_double_click_snapping), - mResizingView(p.resizing_view), - mResizeListener(NULL), - mImagePanel(NULL) -{ - setFollowsNone(); - // set up some generically good follow code. - switch( mSide ) - { - case LEFT: - setFollowsLeft(); - setFollowsTop(); - setFollowsBottom(); - break; - case TOP: - setFollowsTop(); - setFollowsLeft(); - setFollowsRight(); - break; - case RIGHT: - setFollowsRight(); - setFollowsTop(); - setFollowsBottom(); - break; - case BOTTOM: - setFollowsBottom(); - setFollowsLeft(); - setFollowsRight(); - break; - default: - break; - } -} - -bool LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (!canResize()) return false; - - // Route future Mouse messages here preemptively. (Release on mouse up.) - // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture( this ); - - localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); - mLastMouseScreenX = mDragLastScreenX; - mLastMouseScreenY = mDragLastScreenY; - - return true; -} - - -bool LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - if( hasMouseCapture() ) - { - // Release the mouse - gFocusMgr.setMouseCapture( NULL ); - handled = true; - } - else - { - handled = true; - } - return handled; -} - - -bool LLResizeBar::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // We only handle the click if the click both started and ended within us - if( hasMouseCapture() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y); - - S32 delta_x = screen_x - mDragLastScreenX; - S32 delta_y = screen_y - mDragLastScreenY; - - LLCoordGL mouse_dir; - // use hysteresis on mouse motion to preserve user intent when mouse stops moving - mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; - mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; - mLastMouseDir = mouse_dir; - mLastMouseScreenX = screen_x; - mLastMouseScreenY = screen_y; - - // Make sure the mouse in still over the application. We don't want to make the parent - // so big that we can't see the resize handle any more. - LLRect valid_rect = getRootView()->getRect(); - - if( valid_rect.localPointInRect( screen_x, screen_y ) && mResizingView ) - { - // Resize the parent - LLRect orig_rect = mResizingView->getRect(); - LLRect scaled_rect = orig_rect; - - S32 new_width = orig_rect.getWidth(); - S32 new_height = orig_rect.getHeight(); - - switch( mSide ) - { - case LEFT: - new_width = llclamp(orig_rect.getWidth() - delta_x, mMinSize, mMaxSize); - delta_x = orig_rect.getWidth() - new_width; - scaled_rect.translate(delta_x, 0); - break; - - case TOP: - new_height = llclamp(orig_rect.getHeight() + delta_y, mMinSize, mMaxSize); - delta_y = new_height - orig_rect.getHeight(); - break; - - case RIGHT: - new_width = llclamp(orig_rect.getWidth() + delta_x, mMinSize, mMaxSize); - delta_x = new_width - orig_rect.getWidth(); - break; - - case BOTTOM: - new_height = llclamp(orig_rect.getHeight() - delta_y, mMinSize, mMaxSize); - delta_y = orig_rect.getHeight() - new_height; - scaled_rect.translate(0, delta_y); - break; - } - - notifyParent(LLSD().with("action", "resize") - .with("view_name", mResizingView->getName()) - .with("new_height", new_height) - .with("new_width", new_width)); - - scaled_rect.mTop = scaled_rect.mBottom + new_height; - scaled_rect.mRight = scaled_rect.mLeft + new_width; - mResizingView->setRect(scaled_rect); - - LLView* snap_view = NULL; - - if (mSnappingEnabled) - { - static LLUICachedControl snap_margin ("SnapMargin", 0); - switch( mSide ) - { - case LEFT: - snap_view = mResizingView->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin); - break; - case TOP: - snap_view = mResizingView->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin); - break; - case RIGHT: - snap_view = mResizingView->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin); - break; - case BOTTOM: - snap_view = mResizingView->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin); - break; - } - } - - // register "snap" behavior with snapped view - mResizingView->setSnappedTo(snap_view); - - // restore original rectangle so the appropriate changes are detected - mResizingView->setRect(orig_rect); - // change view shape as user operation - mResizingView->setShape(scaled_rect, true); - - // update last valid mouse cursor position based on resized view's actual size - LLRect new_rect = mResizingView->getRect(); - - switch(mSide) - { - case LEFT: - { - S32 actual_delta_x = new_rect.mLeft - orig_rect.mLeft; - if (actual_delta_x != delta_x) - { - // restore everything by left - new_rect.mBottom = orig_rect.mBottom; - new_rect.mTop = orig_rect.mTop; - new_rect.mRight = orig_rect.mRight; - mResizingView->setShape(new_rect, true); - } - mDragLastScreenX += actual_delta_x; - - break; - } - case RIGHT: - { - S32 actual_delta_x = new_rect.mRight - orig_rect.mRight; - if (actual_delta_x != delta_x) - { - // restore everything by left - new_rect.mBottom = orig_rect.mBottom; - new_rect.mTop = orig_rect.mTop; - new_rect.mLeft = orig_rect.mLeft; - mResizingView->setShape(new_rect, true); - } - mDragLastScreenX += new_rect.mRight - orig_rect.mRight; - break; - } - case TOP: - { - S32 actual_delta_y = new_rect.mTop - orig_rect.mTop; - if (actual_delta_y != delta_y) - { - // restore everything by left - new_rect.mBottom = orig_rect.mBottom; - new_rect.mLeft = orig_rect.mLeft; - new_rect.mRight = orig_rect.mRight; - mResizingView->setShape(new_rect, true); - } - mDragLastScreenY += new_rect.mTop - orig_rect.mTop; - break; - } - case BOTTOM: - { - S32 actual_delta_y = new_rect.mBottom - orig_rect.mBottom; - if (actual_delta_y != delta_y) - { - // restore everything by left - new_rect.mTop = orig_rect.mTop; - new_rect.mLeft = orig_rect.mLeft; - new_rect.mRight = orig_rect.mRight; - mResizingView->setShape(new_rect, true); - } - mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom; - break; - } - default: - break; - } - } - - handled = true; - } - else - { - handled = true; - } - - if( handled && canResize() ) - { - switch( mSide ) - { - case LEFT: - case RIGHT: - getWindow()->setCursor(UI_CURSOR_SIZEWE); - break; - - case TOP: - case BOTTOM: - getWindow()->setCursor(UI_CURSOR_SIZENS); - break; - } - } - - if (mResizeListener) - { - mResizeListener(NULL); - } - - return handled; -} // end LLResizeBar::handleHover - -bool LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - LLRect orig_rect = mResizingView->getRect(); - LLRect scaled_rect = orig_rect; - - if (mSnappingEnabled && mAllowDoubleClickSnapping) - { - switch( mSide ) - { - case LEFT: - mResizingView->findSnapEdge(scaled_rect.mLeft, LLCoordGL(0, 0), SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, S32_MAX); - scaled_rect.mLeft = scaled_rect.mRight - llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize); - break; - case TOP: - mResizingView->findSnapEdge(scaled_rect.mTop, LLCoordGL(0, 0), SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, S32_MAX); - scaled_rect.mTop = scaled_rect.mBottom + llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize); - break; - case RIGHT: - mResizingView->findSnapEdge(scaled_rect.mRight, LLCoordGL(0, 0), SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, S32_MAX); - scaled_rect.mRight = scaled_rect.mLeft + llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize); - break; - case BOTTOM: - mResizingView->findSnapEdge(scaled_rect.mBottom, LLCoordGL(0, 0), SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, S32_MAX); - scaled_rect.mBottom = scaled_rect.mTop - llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize); - break; - } - - mResizingView->setShape(scaled_rect, true); - } - - return true; -} - -void LLResizeBar::setImagePanel(LLPanel * panelp) -{ - const LLView::child_list_t * children = getChildList(); - if (getChildCount() == 2) - { - LLPanel * image_panelp = dynamic_cast(children->back()); - if (image_panelp) - { - removeChild(image_panelp); - delete image_panelp; - } - } - - addChild(panelp); - sendChildToBack(panelp); -} - -LLPanel * LLResizeBar::getImagePanel() const -{ - return getChildCount() > 0 ? (LLPanel *)getChildList()->back() : NULL; -} +/** + * @file llresizebar.cpp + * @brief LLResizeBar base class + * + * $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 "linden_common.h" + +#include "llresizebar.h" + +#include "lllocalcliprect.h" +#include "llmath.h" +#include "llui.h" +#include "llmenugl.h" +#include "llfocusmgr.h" +#include "llwindow.h" + +LLResizeBar::Params::Params() +: max_size("max_size", S32_MAX), + snapping_enabled("snapping_enabled", true), + resizing_view("resizing_view"), + side("side"), + allow_double_click_snapping("allow_double_click_snapping", true) +{ + name = "resize_bar"; +} + +LLResizeBar::LLResizeBar(const LLResizeBar::Params& p) +: LLView(p), + mDragLastScreenX( 0 ), + mDragLastScreenY( 0 ), + mLastMouseScreenX( 0 ), + mLastMouseScreenY( 0 ), + mMinSize( p.min_size ), + mMaxSize( p.max_size ), + mSide( p.side ), + mSnappingEnabled(p.snapping_enabled), + mAllowDoubleClickSnapping(p.allow_double_click_snapping), + mResizingView(p.resizing_view), + mResizeListener(NULL), + mImagePanel(NULL) +{ + setFollowsNone(); + // set up some generically good follow code. + switch( mSide ) + { + case LEFT: + setFollowsLeft(); + setFollowsTop(); + setFollowsBottom(); + break; + case TOP: + setFollowsTop(); + setFollowsLeft(); + setFollowsRight(); + break; + case RIGHT: + setFollowsRight(); + setFollowsTop(); + setFollowsBottom(); + break; + case BOTTOM: + setFollowsBottom(); + setFollowsLeft(); + setFollowsRight(); + break; + default: + break; + } +} + +bool LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (!canResize()) return false; + + // Route future Mouse messages here preemptively. (Release on mouse up.) + // No handler needed for focus lost since this clas has no state that depends on it. + gFocusMgr.setMouseCapture( this ); + + localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); + mLastMouseScreenX = mDragLastScreenX; + mLastMouseScreenY = mDragLastScreenY; + + return true; +} + + +bool LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if( hasMouseCapture() ) + { + // Release the mouse + gFocusMgr.setMouseCapture( NULL ); + handled = true; + } + else + { + handled = true; + } + return handled; +} + + +bool LLResizeBar::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // We only handle the click if the click both started and ended within us + if( hasMouseCapture() ) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + S32 delta_x = screen_x - mDragLastScreenX; + S32 delta_y = screen_y - mDragLastScreenY; + + LLCoordGL mouse_dir; + // use hysteresis on mouse motion to preserve user intent when mouse stops moving + mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; + mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; + mLastMouseDir = mouse_dir; + mLastMouseScreenX = screen_x; + mLastMouseScreenY = screen_y; + + // Make sure the mouse in still over the application. We don't want to make the parent + // so big that we can't see the resize handle any more. + LLRect valid_rect = getRootView()->getRect(); + + if( valid_rect.localPointInRect( screen_x, screen_y ) && mResizingView ) + { + // Resize the parent + LLRect orig_rect = mResizingView->getRect(); + LLRect scaled_rect = orig_rect; + + S32 new_width = orig_rect.getWidth(); + S32 new_height = orig_rect.getHeight(); + + switch( mSide ) + { + case LEFT: + new_width = llclamp(orig_rect.getWidth() - delta_x, mMinSize, mMaxSize); + delta_x = orig_rect.getWidth() - new_width; + scaled_rect.translate(delta_x, 0); + break; + + case TOP: + new_height = llclamp(orig_rect.getHeight() + delta_y, mMinSize, mMaxSize); + delta_y = new_height - orig_rect.getHeight(); + break; + + case RIGHT: + new_width = llclamp(orig_rect.getWidth() + delta_x, mMinSize, mMaxSize); + delta_x = new_width - orig_rect.getWidth(); + break; + + case BOTTOM: + new_height = llclamp(orig_rect.getHeight() - delta_y, mMinSize, mMaxSize); + delta_y = orig_rect.getHeight() - new_height; + scaled_rect.translate(0, delta_y); + break; + } + + notifyParent(LLSD().with("action", "resize") + .with("view_name", mResizingView->getName()) + .with("new_height", new_height) + .with("new_width", new_width)); + + scaled_rect.mTop = scaled_rect.mBottom + new_height; + scaled_rect.mRight = scaled_rect.mLeft + new_width; + mResizingView->setRect(scaled_rect); + + LLView* snap_view = NULL; + + if (mSnappingEnabled) + { + static LLUICachedControl snap_margin ("SnapMargin", 0); + switch( mSide ) + { + case LEFT: + snap_view = mResizingView->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin); + break; + case TOP: + snap_view = mResizingView->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin); + break; + case RIGHT: + snap_view = mResizingView->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin); + break; + case BOTTOM: + snap_view = mResizingView->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin); + break; + } + } + + // register "snap" behavior with snapped view + mResizingView->setSnappedTo(snap_view); + + // restore original rectangle so the appropriate changes are detected + mResizingView->setRect(orig_rect); + // change view shape as user operation + mResizingView->setShape(scaled_rect, true); + + // update last valid mouse cursor position based on resized view's actual size + LLRect new_rect = mResizingView->getRect(); + + switch(mSide) + { + case LEFT: + { + S32 actual_delta_x = new_rect.mLeft - orig_rect.mLeft; + if (actual_delta_x != delta_x) + { + // restore everything by left + new_rect.mBottom = orig_rect.mBottom; + new_rect.mTop = orig_rect.mTop; + new_rect.mRight = orig_rect.mRight; + mResizingView->setShape(new_rect, true); + } + mDragLastScreenX += actual_delta_x; + + break; + } + case RIGHT: + { + S32 actual_delta_x = new_rect.mRight - orig_rect.mRight; + if (actual_delta_x != delta_x) + { + // restore everything by left + new_rect.mBottom = orig_rect.mBottom; + new_rect.mTop = orig_rect.mTop; + new_rect.mLeft = orig_rect.mLeft; + mResizingView->setShape(new_rect, true); + } + mDragLastScreenX += new_rect.mRight - orig_rect.mRight; + break; + } + case TOP: + { + S32 actual_delta_y = new_rect.mTop - orig_rect.mTop; + if (actual_delta_y != delta_y) + { + // restore everything by left + new_rect.mBottom = orig_rect.mBottom; + new_rect.mLeft = orig_rect.mLeft; + new_rect.mRight = orig_rect.mRight; + mResizingView->setShape(new_rect, true); + } + mDragLastScreenY += new_rect.mTop - orig_rect.mTop; + break; + } + case BOTTOM: + { + S32 actual_delta_y = new_rect.mBottom - orig_rect.mBottom; + if (actual_delta_y != delta_y) + { + // restore everything by left + new_rect.mTop = orig_rect.mTop; + new_rect.mLeft = orig_rect.mLeft; + new_rect.mRight = orig_rect.mRight; + mResizingView->setShape(new_rect, true); + } + mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom; + break; + } + default: + break; + } + } + + handled = true; + } + else + { + handled = true; + } + + if( handled && canResize() ) + { + switch( mSide ) + { + case LEFT: + case RIGHT: + getWindow()->setCursor(UI_CURSOR_SIZEWE); + break; + + case TOP: + case BOTTOM: + getWindow()->setCursor(UI_CURSOR_SIZENS); + break; + } + } + + if (mResizeListener) + { + mResizeListener(NULL); + } + + return handled; +} // end LLResizeBar::handleHover + +bool LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + LLRect orig_rect = mResizingView->getRect(); + LLRect scaled_rect = orig_rect; + + if (mSnappingEnabled && mAllowDoubleClickSnapping) + { + switch( mSide ) + { + case LEFT: + mResizingView->findSnapEdge(scaled_rect.mLeft, LLCoordGL(0, 0), SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, S32_MAX); + scaled_rect.mLeft = scaled_rect.mRight - llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize); + break; + case TOP: + mResizingView->findSnapEdge(scaled_rect.mTop, LLCoordGL(0, 0), SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, S32_MAX); + scaled_rect.mTop = scaled_rect.mBottom + llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize); + break; + case RIGHT: + mResizingView->findSnapEdge(scaled_rect.mRight, LLCoordGL(0, 0), SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, S32_MAX); + scaled_rect.mRight = scaled_rect.mLeft + llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize); + break; + case BOTTOM: + mResizingView->findSnapEdge(scaled_rect.mBottom, LLCoordGL(0, 0), SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, S32_MAX); + scaled_rect.mBottom = scaled_rect.mTop - llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize); + break; + } + + mResizingView->setShape(scaled_rect, true); + } + + return true; +} + +void LLResizeBar::setImagePanel(LLPanel * panelp) +{ + const LLView::child_list_t * children = getChildList(); + if (getChildCount() == 2) + { + LLPanel * image_panelp = dynamic_cast(children->back()); + if (image_panelp) + { + removeChild(image_panelp); + delete image_panelp; + } + } + + addChild(panelp); + sendChildToBack(panelp); +} + +LLPanel * LLResizeBar::getImagePanel() const +{ + return getChildCount() > 0 ? (LLPanel *)getChildList()->back() : NULL; +} diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h index 7a6d3fa4c0..4b0f435834 100644 --- a/indra/llui/llresizebar.h +++ b/indra/llui/llresizebar.h @@ -1,88 +1,88 @@ -/** - * @file llresizebar.h - * @brief LLResizeBar base class - * - * $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$ - */ - -#ifndef LL_RESIZEBAR_H -#define LL_RESIZEBAR_H - -#include "llview.h" - -class LLResizeBar : public LLView -{ -public: - enum Side { LEFT, TOP, RIGHT, BOTTOM }; - - struct Params : public LLInitParam::Block - { - Mandatory resizing_view; - Mandatory side; - - Optional min_size; - Optional max_size; - Optional snapping_enabled; - Optional allow_double_click_snapping; - - Params(); - }; - -protected: - LLResizeBar(const LLResizeBar::Params& p); - friend class LLUICtrlFactory; - -public: - - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); - - void setResizeLimits( S32 min_size, S32 max_size ) { mMinSize = min_size; mMaxSize = max_size; } - void setEnableSnapping(bool enable) { mSnappingEnabled = enable; } - void setAllowDoubleClickSnapping(bool allow) { mAllowDoubleClickSnapping = allow; } - bool canResize() { return getEnabled() && mMaxSize > mMinSize; } - void setResizeListener(boost::function listener) {mResizeListener = listener;} - void setImagePanel(LLPanel * panelp); - LLPanel * getImagePanel() const; - -private: - S32 mDragLastScreenX; - S32 mDragLastScreenY; - S32 mLastMouseScreenX; - S32 mLastMouseScreenY; - LLCoordGL mLastMouseDir; - S32 mMinSize; - S32 mMaxSize; - const Side mSide; - bool mSnappingEnabled, - mAllowDoubleClickSnapping; - LLView* mResizingView; - boost::function mResizeListener; - LLPointer mDragHandleImage; - LLPanel * mImagePanel; -}; - -#endif // LL_RESIZEBAR_H - - +/** + * @file llresizebar.h + * @brief LLResizeBar base class + * + * $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$ + */ + +#ifndef LL_RESIZEBAR_H +#define LL_RESIZEBAR_H + +#include "llview.h" + +class LLResizeBar : public LLView +{ +public: + enum Side { LEFT, TOP, RIGHT, BOTTOM }; + + struct Params : public LLInitParam::Block + { + Mandatory resizing_view; + Mandatory side; + + Optional min_size; + Optional max_size; + Optional snapping_enabled; + Optional allow_double_click_snapping; + + Params(); + }; + +protected: + LLResizeBar(const LLResizeBar::Params& p); + friend class LLUICtrlFactory; + +public: + + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); + + void setResizeLimits( S32 min_size, S32 max_size ) { mMinSize = min_size; mMaxSize = max_size; } + void setEnableSnapping(bool enable) { mSnappingEnabled = enable; } + void setAllowDoubleClickSnapping(bool allow) { mAllowDoubleClickSnapping = allow; } + bool canResize() { return getEnabled() && mMaxSize > mMinSize; } + void setResizeListener(boost::function listener) {mResizeListener = listener;} + void setImagePanel(LLPanel * panelp); + LLPanel * getImagePanel() const; + +private: + S32 mDragLastScreenX; + S32 mDragLastScreenY; + S32 mLastMouseScreenX; + S32 mLastMouseScreenY; + LLCoordGL mLastMouseDir; + S32 mMinSize; + S32 mMaxSize; + const Side mSide; + bool mSnappingEnabled, + mAllowDoubleClickSnapping; + LLView* mResizingView; + boost::function mResizeListener; + LLPointer mDragHandleImage; + LLPanel * mImagePanel; +}; + +#endif // LL_RESIZEBAR_H + + diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 55eeae6963..0b7bb55360 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -1,385 +1,385 @@ -/** - * @file llresizehandle.cpp - * @brief LLResizeHandle base class - * - * $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 "linden_common.h" - -#include "llresizehandle.h" - -#include "llfocusmgr.h" -#include "llmath.h" -#include "llui.h" -#include "llmenugl.h" -#include "llcontrol.h" -#include "llfloater.h" -#include "llwindow.h" - -const S32 RESIZE_BORDER_WIDTH = 3; - -LLResizeHandle::Params::Params() -: corner("corner"), - min_width("min_width"), - min_height("min_height") -{ - name = "resize_handle"; -} - -LLResizeHandle::LLResizeHandle(const LLResizeHandle::Params& p) -: LLView(p), - mDragLastScreenX( 0 ), - mDragLastScreenY( 0 ), - mLastMouseScreenX( 0 ), - mLastMouseScreenY( 0 ), - mImage( NULL ), - mMinWidth( p.min_width ), - mMinHeight( p.min_height ), - mCorner( p.corner ) -{ - if( RIGHT_BOTTOM == mCorner) - { - mImage = LLUI::getUIImage("Resize_Corner"); - } - switch( p.corner ) - { - case LEFT_TOP: setFollows( FOLLOWS_LEFT | FOLLOWS_TOP ); break; - case LEFT_BOTTOM: setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); break; - case RIGHT_TOP: setFollows( FOLLOWS_RIGHT | FOLLOWS_TOP ); break; - case RIGHT_BOTTOM: setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); break; - } -} - -LLResizeHandle::~LLResizeHandle() -{ - gFocusMgr.removeKeyboardFocusWithoutCallback(this); -} - - -bool LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = false; - if( pointInHandle(x, y) ) - { - handled = true; - // Route future Mouse messages here preemptively. (Release on mouse up.) - // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture( this ); - - localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); - mLastMouseScreenX = mDragLastScreenX; - mLastMouseScreenY = mDragLastScreenY; - } - - return handled; -} - - -bool LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - if( hasMouseCapture() ) - { - // Release the mouse - gFocusMgr.setMouseCapture( NULL ); - handled = true; - } - else if( pointInHandle(x, y) ) - { - handled = true; - } - - return handled; -} - - -bool LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // We only handle the click if the click both started and ended within us - if( hasMouseCapture() ) - { - // Make sure the mouse in still over the application. We don't want to make the parent - // so big that we can't see the resize handle any more. - - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y); - const LLRect valid_rect = getRootView()->getRect(); - screen_x = llclamp( screen_x, valid_rect.mLeft, valid_rect.mRight ); - screen_y = llclamp( screen_y, valid_rect.mBottom, valid_rect.mTop ); - - LLView* resizing_view = getParent(); - if( resizing_view ) - { - // undock floater when user resize it - LLFloater* floater_parent = dynamic_cast(getParent()); - if (floater_parent && floater_parent->isDocked()) - { - floater_parent->setDocked(false, false); - } - - // Resize the parent - LLRect orig_rect = resizing_view->getRect(); - LLRect scaled_rect = orig_rect; - S32 delta_x = screen_x - mDragLastScreenX; - S32 delta_y = screen_y - mDragLastScreenY; - LLCoordGL mouse_dir; - // use hysteresis on mouse motion to preserve user intent when mouse stops moving - mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; - mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; - mLastMouseScreenX = screen_x; - mLastMouseScreenY = screen_y; - mLastMouseDir = mouse_dir; - - S32 x_multiple = 1; - S32 y_multiple = 1; - switch( mCorner ) - { - case LEFT_TOP: - x_multiple = -1; - y_multiple = 1; - break; - case LEFT_BOTTOM: - x_multiple = -1; - y_multiple = -1; - break; - case RIGHT_TOP: - x_multiple = 1; - y_multiple = 1; - break; - case RIGHT_BOTTOM: - x_multiple = 1; - y_multiple = -1; - break; - } - - S32 new_width = orig_rect.getWidth() + x_multiple * delta_x; - if( new_width < mMinWidth ) - { - new_width = mMinWidth; - delta_x = x_multiple * (mMinWidth - orig_rect.getWidth()); - } - - S32 new_height = orig_rect.getHeight() + y_multiple * delta_y; - if( new_height < mMinHeight ) - { - new_height = mMinHeight; - delta_y = y_multiple * (mMinHeight - orig_rect.getHeight()); - } - - switch( mCorner ) - { - case LEFT_TOP: - scaled_rect.translate(delta_x, 0); - break; - case LEFT_BOTTOM: - scaled_rect.translate(delta_x, delta_y); - break; - case RIGHT_TOP: - break; - case RIGHT_BOTTOM: - scaled_rect.translate(0, delta_y); - break; - } - - // temporarily set new parent rect - scaled_rect.mRight = scaled_rect.mLeft + new_width; - scaled_rect.mTop = scaled_rect.mBottom + new_height; - resizing_view->setRect(scaled_rect); - - LLView* snap_view = NULL; - LLView* test_view = NULL; - - static LLUICachedControl snap_margin ("SnapMargin", 0); - // now do snapping - switch(mCorner) - { - case LEFT_TOP: - snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin); - test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin); - if (!snap_view) - { - snap_view = test_view; - } - break; - case LEFT_BOTTOM: - snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin); - test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin); - if (!snap_view) - { - snap_view = test_view; - } - break; - case RIGHT_TOP: - snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin); - test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin); - if (!snap_view) - { - snap_view = test_view; - } - break; - case RIGHT_BOTTOM: - snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin); - test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin); - if (!snap_view) - { - snap_view = test_view; - } - break; - } - - // register "snap" behavior with snapped view - resizing_view->setSnappedTo(snap_view); - - // reset parent rect - resizing_view->setRect(orig_rect); - - // translate and scale to new shape - resizing_view->setShape(scaled_rect, true); - - // update last valid mouse cursor position based on resized view's actual size - LLRect new_rect = resizing_view->getRect(); - S32 actual_delta_x = 0; - S32 actual_delta_y = 0; - switch(mCorner) - { - case LEFT_TOP: - actual_delta_x = new_rect.mLeft - orig_rect.mLeft; - actual_delta_y = new_rect.mTop - orig_rect.mTop; - if (actual_delta_x != delta_x - || actual_delta_y != delta_y) - { - new_rect.mRight = orig_rect.mRight; - new_rect.mBottom = orig_rect.mBottom; - resizing_view->setShape(new_rect, true); - } - - mDragLastScreenX += actual_delta_x; - mDragLastScreenY += actual_delta_y; - break; - case LEFT_BOTTOM: - actual_delta_x = new_rect.mLeft - orig_rect.mLeft; - actual_delta_y = new_rect.mBottom - orig_rect.mBottom; - if (actual_delta_x != delta_x - || actual_delta_y != delta_y) - { - new_rect.mRight = orig_rect.mRight; - new_rect.mTop = orig_rect.mTop; - resizing_view->setShape(new_rect, true); - } - - mDragLastScreenX += actual_delta_x; - mDragLastScreenY += actual_delta_y; - break; - case RIGHT_TOP: - actual_delta_x = new_rect.mRight - orig_rect.mRight; - actual_delta_y = new_rect.mTop - orig_rect.mTop; - if (actual_delta_x != delta_x - || actual_delta_y != delta_y) - { - new_rect.mLeft = orig_rect.mLeft; - new_rect.mBottom = orig_rect.mBottom; - resizing_view->setShape(new_rect, true); - } - - mDragLastScreenX += actual_delta_x; - mDragLastScreenY += actual_delta_y; - break; - case RIGHT_BOTTOM: - actual_delta_x = new_rect.mRight - orig_rect.mRight; - actual_delta_y = new_rect.mBottom - orig_rect.mBottom; - if (actual_delta_x != delta_x - || actual_delta_y != delta_y) - { - new_rect.mLeft = orig_rect.mLeft; - new_rect.mTop = orig_rect.mTop; - resizing_view->setShape(new_rect, true); - } - - mDragLastScreenX += actual_delta_x; - mDragLastScreenY += actual_delta_y; - break; - default: - break; - } - } - - handled = true; - } - else // don't have mouse capture - { - if( pointInHandle( x, y ) ) - { - handled = true; - } - } - - if( handled ) - { - switch( mCorner ) - { - case RIGHT_BOTTOM: - case LEFT_TOP: - getWindow()->setCursor(UI_CURSOR_SIZENWSE); - break; - case LEFT_BOTTOM: - case RIGHT_TOP: - getWindow()->setCursor(UI_CURSOR_SIZENESW); - break; - } - } - - return handled; -} // end handleHover - - -// assumes GL state is set for 2D -void LLResizeHandle::draw() -{ - if( mImage.notNull() && getVisible() && (RIGHT_BOTTOM == mCorner) ) - { - mImage->draw(0, 0); - } -} - - -bool LLResizeHandle::pointInHandle( S32 x, S32 y ) -{ - if( pointInView(x, y) ) - { - const S32 TOP_BORDER = (getRect().getHeight() - RESIZE_BORDER_WIDTH); - const S32 RIGHT_BORDER = (getRect().getWidth() - RESIZE_BORDER_WIDTH); - - switch( mCorner ) - { - case LEFT_TOP: return (x <= RESIZE_BORDER_WIDTH) || (y >= TOP_BORDER); - case LEFT_BOTTOM: return (x <= RESIZE_BORDER_WIDTH) || (y <= RESIZE_BORDER_WIDTH); - case RIGHT_TOP: return (x >= RIGHT_BORDER) || (y >= TOP_BORDER); - case RIGHT_BOTTOM: return true; - } - } - return false; -} +/** + * @file llresizehandle.cpp + * @brief LLResizeHandle base class + * + * $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 "linden_common.h" + +#include "llresizehandle.h" + +#include "llfocusmgr.h" +#include "llmath.h" +#include "llui.h" +#include "llmenugl.h" +#include "llcontrol.h" +#include "llfloater.h" +#include "llwindow.h" + +const S32 RESIZE_BORDER_WIDTH = 3; + +LLResizeHandle::Params::Params() +: corner("corner"), + min_width("min_width"), + min_height("min_height") +{ + name = "resize_handle"; +} + +LLResizeHandle::LLResizeHandle(const LLResizeHandle::Params& p) +: LLView(p), + mDragLastScreenX( 0 ), + mDragLastScreenY( 0 ), + mLastMouseScreenX( 0 ), + mLastMouseScreenY( 0 ), + mImage( NULL ), + mMinWidth( p.min_width ), + mMinHeight( p.min_height ), + mCorner( p.corner ) +{ + if( RIGHT_BOTTOM == mCorner) + { + mImage = LLUI::getUIImage("Resize_Corner"); + } + switch( p.corner ) + { + case LEFT_TOP: setFollows( FOLLOWS_LEFT | FOLLOWS_TOP ); break; + case LEFT_BOTTOM: setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); break; + case RIGHT_TOP: setFollows( FOLLOWS_RIGHT | FOLLOWS_TOP ); break; + case RIGHT_BOTTOM: setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); break; + } +} + +LLResizeHandle::~LLResizeHandle() +{ + gFocusMgr.removeKeyboardFocusWithoutCallback(this); +} + + +bool LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = false; + if( pointInHandle(x, y) ) + { + handled = true; + // Route future Mouse messages here preemptively. (Release on mouse up.) + // No handler needed for focus lost since this clas has no state that depends on it. + gFocusMgr.setMouseCapture( this ); + + localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); + mLastMouseScreenX = mDragLastScreenX; + mLastMouseScreenY = mDragLastScreenY; + } + + return handled; +} + + +bool LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if( hasMouseCapture() ) + { + // Release the mouse + gFocusMgr.setMouseCapture( NULL ); + handled = true; + } + else if( pointInHandle(x, y) ) + { + handled = true; + } + + return handled; +} + + +bool LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // We only handle the click if the click both started and ended within us + if( hasMouseCapture() ) + { + // Make sure the mouse in still over the application. We don't want to make the parent + // so big that we can't see the resize handle any more. + + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + const LLRect valid_rect = getRootView()->getRect(); + screen_x = llclamp( screen_x, valid_rect.mLeft, valid_rect.mRight ); + screen_y = llclamp( screen_y, valid_rect.mBottom, valid_rect.mTop ); + + LLView* resizing_view = getParent(); + if( resizing_view ) + { + // undock floater when user resize it + LLFloater* floater_parent = dynamic_cast(getParent()); + if (floater_parent && floater_parent->isDocked()) + { + floater_parent->setDocked(false, false); + } + + // Resize the parent + LLRect orig_rect = resizing_view->getRect(); + LLRect scaled_rect = orig_rect; + S32 delta_x = screen_x - mDragLastScreenX; + S32 delta_y = screen_y - mDragLastScreenY; + LLCoordGL mouse_dir; + // use hysteresis on mouse motion to preserve user intent when mouse stops moving + mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; + mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; + mLastMouseScreenX = screen_x; + mLastMouseScreenY = screen_y; + mLastMouseDir = mouse_dir; + + S32 x_multiple = 1; + S32 y_multiple = 1; + switch( mCorner ) + { + case LEFT_TOP: + x_multiple = -1; + y_multiple = 1; + break; + case LEFT_BOTTOM: + x_multiple = -1; + y_multiple = -1; + break; + case RIGHT_TOP: + x_multiple = 1; + y_multiple = 1; + break; + case RIGHT_BOTTOM: + x_multiple = 1; + y_multiple = -1; + break; + } + + S32 new_width = orig_rect.getWidth() + x_multiple * delta_x; + if( new_width < mMinWidth ) + { + new_width = mMinWidth; + delta_x = x_multiple * (mMinWidth - orig_rect.getWidth()); + } + + S32 new_height = orig_rect.getHeight() + y_multiple * delta_y; + if( new_height < mMinHeight ) + { + new_height = mMinHeight; + delta_y = y_multiple * (mMinHeight - orig_rect.getHeight()); + } + + switch( mCorner ) + { + case LEFT_TOP: + scaled_rect.translate(delta_x, 0); + break; + case LEFT_BOTTOM: + scaled_rect.translate(delta_x, delta_y); + break; + case RIGHT_TOP: + break; + case RIGHT_BOTTOM: + scaled_rect.translate(0, delta_y); + break; + } + + // temporarily set new parent rect + scaled_rect.mRight = scaled_rect.mLeft + new_width; + scaled_rect.mTop = scaled_rect.mBottom + new_height; + resizing_view->setRect(scaled_rect); + + LLView* snap_view = NULL; + LLView* test_view = NULL; + + static LLUICachedControl snap_margin ("SnapMargin", 0); + // now do snapping + switch(mCorner) + { + case LEFT_TOP: + snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin); + test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin); + if (!snap_view) + { + snap_view = test_view; + } + break; + case LEFT_BOTTOM: + snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin); + test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin); + if (!snap_view) + { + snap_view = test_view; + } + break; + case RIGHT_TOP: + snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin); + test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin); + if (!snap_view) + { + snap_view = test_view; + } + break; + case RIGHT_BOTTOM: + snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin); + test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin); + if (!snap_view) + { + snap_view = test_view; + } + break; + } + + // register "snap" behavior with snapped view + resizing_view->setSnappedTo(snap_view); + + // reset parent rect + resizing_view->setRect(orig_rect); + + // translate and scale to new shape + resizing_view->setShape(scaled_rect, true); + + // update last valid mouse cursor position based on resized view's actual size + LLRect new_rect = resizing_view->getRect(); + S32 actual_delta_x = 0; + S32 actual_delta_y = 0; + switch(mCorner) + { + case LEFT_TOP: + actual_delta_x = new_rect.mLeft - orig_rect.mLeft; + actual_delta_y = new_rect.mTop - orig_rect.mTop; + if (actual_delta_x != delta_x + || actual_delta_y != delta_y) + { + new_rect.mRight = orig_rect.mRight; + new_rect.mBottom = orig_rect.mBottom; + resizing_view->setShape(new_rect, true); + } + + mDragLastScreenX += actual_delta_x; + mDragLastScreenY += actual_delta_y; + break; + case LEFT_BOTTOM: + actual_delta_x = new_rect.mLeft - orig_rect.mLeft; + actual_delta_y = new_rect.mBottom - orig_rect.mBottom; + if (actual_delta_x != delta_x + || actual_delta_y != delta_y) + { + new_rect.mRight = orig_rect.mRight; + new_rect.mTop = orig_rect.mTop; + resizing_view->setShape(new_rect, true); + } + + mDragLastScreenX += actual_delta_x; + mDragLastScreenY += actual_delta_y; + break; + case RIGHT_TOP: + actual_delta_x = new_rect.mRight - orig_rect.mRight; + actual_delta_y = new_rect.mTop - orig_rect.mTop; + if (actual_delta_x != delta_x + || actual_delta_y != delta_y) + { + new_rect.mLeft = orig_rect.mLeft; + new_rect.mBottom = orig_rect.mBottom; + resizing_view->setShape(new_rect, true); + } + + mDragLastScreenX += actual_delta_x; + mDragLastScreenY += actual_delta_y; + break; + case RIGHT_BOTTOM: + actual_delta_x = new_rect.mRight - orig_rect.mRight; + actual_delta_y = new_rect.mBottom - orig_rect.mBottom; + if (actual_delta_x != delta_x + || actual_delta_y != delta_y) + { + new_rect.mLeft = orig_rect.mLeft; + new_rect.mTop = orig_rect.mTop; + resizing_view->setShape(new_rect, true); + } + + mDragLastScreenX += actual_delta_x; + mDragLastScreenY += actual_delta_y; + break; + default: + break; + } + } + + handled = true; + } + else // don't have mouse capture + { + if( pointInHandle( x, y ) ) + { + handled = true; + } + } + + if( handled ) + { + switch( mCorner ) + { + case RIGHT_BOTTOM: + case LEFT_TOP: + getWindow()->setCursor(UI_CURSOR_SIZENWSE); + break; + case LEFT_BOTTOM: + case RIGHT_TOP: + getWindow()->setCursor(UI_CURSOR_SIZENESW); + break; + } + } + + return handled; +} // end handleHover + + +// assumes GL state is set for 2D +void LLResizeHandle::draw() +{ + if( mImage.notNull() && getVisible() && (RIGHT_BOTTOM == mCorner) ) + { + mImage->draw(0, 0); + } +} + + +bool LLResizeHandle::pointInHandle( S32 x, S32 y ) +{ + if( pointInView(x, y) ) + { + const S32 TOP_BORDER = (getRect().getHeight() - RESIZE_BORDER_WIDTH); + const S32 RIGHT_BORDER = (getRect().getWidth() - RESIZE_BORDER_WIDTH); + + switch( mCorner ) + { + case LEFT_TOP: return (x <= RESIZE_BORDER_WIDTH) || (y >= TOP_BORDER); + case LEFT_BOTTOM: return (x <= RESIZE_BORDER_WIDTH) || (y <= RESIZE_BORDER_WIDTH); + case RIGHT_TOP: return (x >= RIGHT_BORDER) || (y >= TOP_BORDER); + case RIGHT_BOTTOM: return true; + } + } + return false; +} diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h index 6f4199a782..9cc4123544 100644 --- a/indra/llui/llresizehandle.h +++ b/indra/llui/llresizehandle.h @@ -1,79 +1,79 @@ -/** - * @file llresizehandle.h - * @brief LLResizeHandle base class - * - * $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$ - */ - -#ifndef LL_RESIZEHANDLE_H -#define LL_RESIZEHANDLE_H - -#include "stdtypes.h" -#include "llview.h" -#include "llcoord.h" - - -class LLResizeHandle : public LLView -{ -public: - enum ECorner { LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM }; - - struct Params : public LLInitParam::Block - { - Mandatory corner; - Optional min_width; - Optional min_height; - Params(); - }; - - ~LLResizeHandle(); -protected: - LLResizeHandle(const LLResizeHandle::Params&); - friend class LLUICtrlFactory; -public: - virtual void draw(); - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - - void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; } - -private: - bool pointInHandle( S32 x, S32 y ); - - S32 mDragLastScreenX; - S32 mDragLastScreenY; - S32 mLastMouseScreenX; - S32 mLastMouseScreenY; - LLCoordGL mLastMouseDir; - LLPointer mImage; - S32 mMinWidth; - S32 mMinHeight; - const ECorner mCorner; -}; - -const S32 RESIZE_HANDLE_HEIGHT = 11; -const S32 RESIZE_HANDLE_WIDTH = 11; - -#endif // LL_RESIZEHANDLE_H - - +/** + * @file llresizehandle.h + * @brief LLResizeHandle base class + * + * $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$ + */ + +#ifndef LL_RESIZEHANDLE_H +#define LL_RESIZEHANDLE_H + +#include "stdtypes.h" +#include "llview.h" +#include "llcoord.h" + + +class LLResizeHandle : public LLView +{ +public: + enum ECorner { LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM }; + + struct Params : public LLInitParam::Block + { + Mandatory corner; + Optional min_width; + Optional min_height; + Params(); + }; + + ~LLResizeHandle(); +protected: + LLResizeHandle(const LLResizeHandle::Params&); + friend class LLUICtrlFactory; +public: + virtual void draw(); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + + void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; } + +private: + bool pointInHandle( S32 x, S32 y ); + + S32 mDragLastScreenX; + S32 mDragLastScreenY; + S32 mLastMouseScreenX; + S32 mLastMouseScreenY; + LLCoordGL mLastMouseDir; + LLPointer mImage; + S32 mMinWidth; + S32 mMinHeight; + const ECorner mCorner; +}; + +const S32 RESIZE_HANDLE_HEIGHT = 11; +const S32 RESIZE_HANDLE_WIDTH = 11; + +#endif // LL_RESIZEHANDLE_H + + diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp index 342842e328..d07aa800d1 100644 --- a/indra/llui/llresmgr.cpp +++ b/indra/llui/llresmgr.cpp @@ -1,267 +1,267 @@ -/** - * @file llresmgr.cpp - * @brief Localized resource manager - * - * $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$ - */ - -// NOTE: this is a MINIMAL implementation. The interface will remain, but the implementation will -// (when the time is right) become dynamic and probably use external files. - -#include "linden_common.h" - -#include "llresmgr.h" -#include "llfontgl.h" -#include "llerror.h" -#include "llstring.h" - - -LLResMgr::LLResMgr() -{ - // Set default - setLocale( LLLOCALE_USA ); -} - - -void LLResMgr::setLocale( LLLOCALE_ID locale_id ) -{ - mLocale = locale_id; -} - -char LLResMgr::getDecimalPoint() const -{ - char decimal = localeconv()->decimal_point[0]; - - return decimal; -} - -char LLResMgr::getThousandsSeparator() const -{ - char separator = localeconv()->thousands_sep[0]; - - return separator; -} - -char LLResMgr::getMonetaryDecimalPoint() const -{ - char decimal = localeconv()->mon_decimal_point[0]; - - return decimal; -} - -char LLResMgr::getMonetaryThousandsSeparator() const -{ - char separator = localeconv()->mon_thousands_sep[0]; - - return separator; -} - - -// Sets output to a string of integers with monetary separators inserted according to the locale. -std::string LLResMgr::getMonetaryString( S32 input ) const -{ - std::string output; - - LLLocale locale(LLLocale::USER_LOCALE); - struct lconv *conv = localeconv(); - - char* negative_sign = conv->negative_sign; - char separator = getMonetaryThousandsSeparator(); - char* grouping = conv->mon_grouping; - - // Note on mon_grouping: - // Specifies a string that defines the size of each group of digits in formatted monetary quantities. - // The operand for the mon_grouping keyword consists of a sequence of semicolon-separated integers. - // Each integer specifies the number of digits in a group. The initial integer defines the size of - // the group immediately to the left of the decimal delimiter. The following integers define succeeding - // groups to the left of the previous group. If the last integer is not -1, the size of the previous - // group (if any) is repeatedly used for the remainder of the digits. If the last integer is -1, no - // further grouping is performed. - - - // Note: we assume here that the currency symbol goes on the left. (Hey, it's Lindens! We can just decide.) - bool negative = (input < 0 ); - bool negative_before = negative && (conv->n_sign_posn != 2); - bool negative_after = negative && (conv->n_sign_posn == 2); - - std::string digits = llformat("%u", abs(input)); - if( !grouping || !grouping[0] ) - { - if( negative_before ) - { - output.append( negative_sign ); - } - output.append( digits ); - if( negative_after ) - { - output.append( negative_sign ); - } - return output; - } - - S32 groupings[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - S32 cur_group; - for( cur_group = 0; grouping[ cur_group ]; cur_group++ ) - { - if( grouping[ cur_group ] != ';' ) - { - groupings[cur_group] = grouping[ cur_group ]; - } - cur_group++; - - if( groupings[cur_group] < 0 ) - { - break; - } - } - S32 group_count = cur_group; - - char reversed_output[20] = ""; /* Flawfinder: ignore */ - char forward_output[20] = ""; /* Flawfinder: ignore */ - S32 output_pos = 0; - - cur_group = 0; - S32 pos = digits.size()-1; - S32 count_within_group = 0; - while( (pos >= 0) && (groupings[cur_group] >= 0) ) - { - count_within_group++; - if( count_within_group > groupings[cur_group] ) - { - count_within_group = 1; - reversed_output[ output_pos++ ] = separator; - - if( (cur_group + 1) >= group_count ) - { - break; - } - else - if( groupings[cur_group + 1] > 0 ) - { - cur_group++; - } - } - reversed_output[ output_pos++ ] = digits[pos--]; - } - - while( pos >= 0 ) - { - reversed_output[ output_pos++ ] = digits[pos--]; - } - - - reversed_output[ output_pos ] = '\0'; - forward_output[ output_pos ] = '\0'; - - for( S32 i = 0; i < output_pos; i++ ) - { - forward_output[ output_pos - 1 - i ] = reversed_output[ i ]; - } - - if( negative_before ) - { - output.append( negative_sign ); - } - output.append( forward_output ); - if( negative_after ) - { - output.append( negative_sign ); - } - return output; -} - -void LLResMgr::getIntegerString( std::string& output, S32 input ) const -{ - // handle special case of input value being zero - if (input == 0) - { - output = "0"; - return; - } - - // *NOTE: this method does not handle negative input integers correctly - S32 fraction = 0; - std::string fraction_string; - S32 remaining_count = input; - while(remaining_count > 0) - { - fraction = (remaining_count) % 1000; - - if (!output.empty()) - { - if (fraction == remaining_count) - { - fraction_string = llformat_to_utf8("%d%c", fraction, getThousandsSeparator()); - } - else - { - fraction_string = llformat_to_utf8("%3.3d%c", fraction, getThousandsSeparator()); - } - output = fraction_string + output; - } - else - { - if (fraction == remaining_count) - { - fraction_string = llformat("%d", fraction); - } - else - { - fraction_string = llformat("%3.3d", fraction); - } - output = fraction_string; - } - remaining_count /= 1000; - } -} - -#if LL_WINDOWS -const std::string LLLocale::USER_LOCALE("English_United States.1252");// = LLStringUtil::null; -const std::string LLLocale::SYSTEM_LOCALE("English_United States.1252"); -#elif LL_DARWIN -const std::string LLLocale::USER_LOCALE("en_US.iso8859-1");// = LLStringUtil::null; -const std::string LLLocale::SYSTEM_LOCALE("en_US.iso8859-1"); -#else // LL_LINUX likes this -const std::string LLLocale::USER_LOCALE("en_US.utf8"); -const std::string LLLocale::SYSTEM_LOCALE("C"); -#endif - - -LLLocale::LLLocale(const std::string& locale_string) -{ - mPrevLocaleString = setlocale( LC_ALL, NULL ); - char* new_locale_string = setlocale( LC_ALL, locale_string.c_str()); - if ( new_locale_string == NULL) - { - LL_WARNS_ONCE("LLLocale") << "Failed to set locale " << locale_string << LL_ENDL; - setlocale(LC_ALL, SYSTEM_LOCALE.c_str()); - } - //else - //{ - // LL_INFOS() << "Set locale to " << new_locale_string << LL_ENDL; - //} -} - -LLLocale::~LLLocale() -{ - setlocale( LC_ALL, mPrevLocaleString.c_str() ); -} +/** + * @file llresmgr.cpp + * @brief Localized resource manager + * + * $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$ + */ + +// NOTE: this is a MINIMAL implementation. The interface will remain, but the implementation will +// (when the time is right) become dynamic and probably use external files. + +#include "linden_common.h" + +#include "llresmgr.h" +#include "llfontgl.h" +#include "llerror.h" +#include "llstring.h" + + +LLResMgr::LLResMgr() +{ + // Set default + setLocale( LLLOCALE_USA ); +} + + +void LLResMgr::setLocale( LLLOCALE_ID locale_id ) +{ + mLocale = locale_id; +} + +char LLResMgr::getDecimalPoint() const +{ + char decimal = localeconv()->decimal_point[0]; + + return decimal; +} + +char LLResMgr::getThousandsSeparator() const +{ + char separator = localeconv()->thousands_sep[0]; + + return separator; +} + +char LLResMgr::getMonetaryDecimalPoint() const +{ + char decimal = localeconv()->mon_decimal_point[0]; + + return decimal; +} + +char LLResMgr::getMonetaryThousandsSeparator() const +{ + char separator = localeconv()->mon_thousands_sep[0]; + + return separator; +} + + +// Sets output to a string of integers with monetary separators inserted according to the locale. +std::string LLResMgr::getMonetaryString( S32 input ) const +{ + std::string output; + + LLLocale locale(LLLocale::USER_LOCALE); + struct lconv *conv = localeconv(); + + char* negative_sign = conv->negative_sign; + char separator = getMonetaryThousandsSeparator(); + char* grouping = conv->mon_grouping; + + // Note on mon_grouping: + // Specifies a string that defines the size of each group of digits in formatted monetary quantities. + // The operand for the mon_grouping keyword consists of a sequence of semicolon-separated integers. + // Each integer specifies the number of digits in a group. The initial integer defines the size of + // the group immediately to the left of the decimal delimiter. The following integers define succeeding + // groups to the left of the previous group. If the last integer is not -1, the size of the previous + // group (if any) is repeatedly used for the remainder of the digits. If the last integer is -1, no + // further grouping is performed. + + + // Note: we assume here that the currency symbol goes on the left. (Hey, it's Lindens! We can just decide.) + bool negative = (input < 0 ); + bool negative_before = negative && (conv->n_sign_posn != 2); + bool negative_after = negative && (conv->n_sign_posn == 2); + + std::string digits = llformat("%u", abs(input)); + if( !grouping || !grouping[0] ) + { + if( negative_before ) + { + output.append( negative_sign ); + } + output.append( digits ); + if( negative_after ) + { + output.append( negative_sign ); + } + return output; + } + + S32 groupings[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + S32 cur_group; + for( cur_group = 0; grouping[ cur_group ]; cur_group++ ) + { + if( grouping[ cur_group ] != ';' ) + { + groupings[cur_group] = grouping[ cur_group ]; + } + cur_group++; + + if( groupings[cur_group] < 0 ) + { + break; + } + } + S32 group_count = cur_group; + + char reversed_output[20] = ""; /* Flawfinder: ignore */ + char forward_output[20] = ""; /* Flawfinder: ignore */ + S32 output_pos = 0; + + cur_group = 0; + S32 pos = digits.size()-1; + S32 count_within_group = 0; + while( (pos >= 0) && (groupings[cur_group] >= 0) ) + { + count_within_group++; + if( count_within_group > groupings[cur_group] ) + { + count_within_group = 1; + reversed_output[ output_pos++ ] = separator; + + if( (cur_group + 1) >= group_count ) + { + break; + } + else + if( groupings[cur_group + 1] > 0 ) + { + cur_group++; + } + } + reversed_output[ output_pos++ ] = digits[pos--]; + } + + while( pos >= 0 ) + { + reversed_output[ output_pos++ ] = digits[pos--]; + } + + + reversed_output[ output_pos ] = '\0'; + forward_output[ output_pos ] = '\0'; + + for( S32 i = 0; i < output_pos; i++ ) + { + forward_output[ output_pos - 1 - i ] = reversed_output[ i ]; + } + + if( negative_before ) + { + output.append( negative_sign ); + } + output.append( forward_output ); + if( negative_after ) + { + output.append( negative_sign ); + } + return output; +} + +void LLResMgr::getIntegerString( std::string& output, S32 input ) const +{ + // handle special case of input value being zero + if (input == 0) + { + output = "0"; + return; + } + + // *NOTE: this method does not handle negative input integers correctly + S32 fraction = 0; + std::string fraction_string; + S32 remaining_count = input; + while(remaining_count > 0) + { + fraction = (remaining_count) % 1000; + + if (!output.empty()) + { + if (fraction == remaining_count) + { + fraction_string = llformat_to_utf8("%d%c", fraction, getThousandsSeparator()); + } + else + { + fraction_string = llformat_to_utf8("%3.3d%c", fraction, getThousandsSeparator()); + } + output = fraction_string + output; + } + else + { + if (fraction == remaining_count) + { + fraction_string = llformat("%d", fraction); + } + else + { + fraction_string = llformat("%3.3d", fraction); + } + output = fraction_string; + } + remaining_count /= 1000; + } +} + +#if LL_WINDOWS +const std::string LLLocale::USER_LOCALE("English_United States.1252");// = LLStringUtil::null; +const std::string LLLocale::SYSTEM_LOCALE("English_United States.1252"); +#elif LL_DARWIN +const std::string LLLocale::USER_LOCALE("en_US.iso8859-1");// = LLStringUtil::null; +const std::string LLLocale::SYSTEM_LOCALE("en_US.iso8859-1"); +#else // LL_LINUX likes this +const std::string LLLocale::USER_LOCALE("en_US.utf8"); +const std::string LLLocale::SYSTEM_LOCALE("C"); +#endif + + +LLLocale::LLLocale(const std::string& locale_string) +{ + mPrevLocaleString = setlocale( LC_ALL, NULL ); + char* new_locale_string = setlocale( LC_ALL, locale_string.c_str()); + if ( new_locale_string == NULL) + { + LL_WARNS_ONCE("LLLocale") << "Failed to set locale " << locale_string << LL_ENDL; + setlocale(LC_ALL, SYSTEM_LOCALE.c_str()); + } + //else + //{ + // LL_INFOS() << "Set locale to " << new_locale_string << LL_ENDL; + //} +} + +LLLocale::~LLLocale() +{ + setlocale( LC_ALL, mPrevLocaleString.c_str() ); +} diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index ab7d0bc38f..9c73b1ba3f 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -1,666 +1,666 @@ -/** - * @file llscrollbar.cpp - * @brief Scrollbar UI widget - * - * $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 "linden_common.h" - -#include "llscrollbar.h" - -#include "llmath.h" -#include "lltimer.h" -#include "v3color.h" - -#include "llbutton.h" -#include "llcriticaldamp.h" -#include "llkeyboard.h" -#include "llui.h" -#include "llfocusmgr.h" -#include "llwindow.h" -#include "llcontrol.h" -#include "llrender.h" -#include "lluictrlfactory.h" - -static LLDefaultChildRegistry::Register register_scrollbar("scroll_bar"); - -LLScrollbar::Params::Params() -: orientation ("orientation", HORIZONTAL), - doc_size ("doc_size", 0), - doc_pos ("doc_pos", 0), - page_size ("page_size", 0), - step_size ("step_size", 1), - thumb_image_vertical("thumb_image_vertical"), - thumb_image_horizontal("thumb_image_horizontal"), - track_image_vertical("track_image_vertical"), - track_image_horizontal("track_image_horizontal"), - track_color("track_color"), - thumb_color("thumb_color"), - thickness("thickness"), - up_button("up_button"), - down_button("down_button"), - left_button("left_button"), - right_button("right_button"), - bg_visible("bg_visible", false), - bg_color("bg_color", LLColor4::black) -{} - -LLScrollbar::LLScrollbar(const Params & p) -: LLUICtrl(p), - mChangeCallback( p.change_callback() ), - mOrientation( p.orientation ), - mDocSize( p.doc_size ), - mDocPos( p.doc_pos ), - mPageSize( p.page_size ), - mStepSize( p.step_size ), - mDocChanged(false), - mDragStartX( 0 ), - mDragStartY( 0 ), - mHoverGlowStrength(0.15f), - mCurGlowStrength(0.f), - mTrackColor( p.track_color() ), - mThumbColor ( p.thumb_color() ), - mThumbImageV(p.thumb_image_vertical), - mThumbImageH(p.thumb_image_horizontal), - mTrackImageV(p.track_image_vertical), - mTrackImageH(p.track_image_horizontal), - mThickness(p.thickness.isProvided() ? p.thickness : LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize")), - mBGVisible(p.bg_visible), - mBGColor(p.bg_color) -{ - updateThumbRect(); - - // Page up and page down buttons - LLRect line_up_rect; - LLRect line_down_rect; - - if( VERTICAL == mOrientation ) - { - line_up_rect.setLeftTopAndSize( 0, getRect().getHeight(), mThickness, mThickness ); - line_down_rect.setOriginAndSize( 0, 0, mThickness, mThickness ); - } - else // HORIZONTAL - { - line_up_rect.setOriginAndSize( 0, 0, mThickness, mThickness ); - line_down_rect.setOriginAndSize( getRect().getWidth() - mThickness, 0, mThickness, mThickness ); - } - - LLButton::Params up_btn(mOrientation == VERTICAL ? p.up_button : p.left_button); - up_btn.name(std::string("Line Up")); - up_btn.rect(line_up_rect); - up_btn.click_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2)); - up_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2)); - up_btn.tab_stop(false); - up_btn.follows.flags = (mOrientation == VERTICAL ? (FOLLOWS_RIGHT | FOLLOWS_TOP) : (FOLLOWS_LEFT | FOLLOWS_BOTTOM)); - - addChild(LLUICtrlFactory::create(up_btn)); - - LLButton::Params down_btn(mOrientation == VERTICAL ? p.down_button : p.right_button); - down_btn.name(std::string("Line Down")); - down_btn.rect(line_down_rect); - down_btn.follows.flags(FOLLOWS_RIGHT|FOLLOWS_BOTTOM); - down_btn.click_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2)); - down_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2)); - down_btn.tab_stop(false); - - addChild(LLUICtrlFactory::create(down_btn)); -} - - -LLScrollbar::~LLScrollbar() -{ - // Children buttons killed by parent class -} - -void LLScrollbar::setDocParams( S32 size, S32 pos ) -{ - mDocSize = size; - setDocPos(pos); - mDocChanged = true; - - updateThumbRect(); -} - -// returns true if document position really changed -bool LLScrollbar::setDocPos(S32 pos, bool update_thumb) -{ - pos = llclamp(pos, 0, getDocPosMax()); - if (pos != mDocPos) - { - mDocPos = pos; - mDocChanged = true; - - if( mChangeCallback ) - { - mChangeCallback( mDocPos, this ); - } - - if( update_thumb ) - { - updateThumbRect(); - } - return true; - } - return false; -} - -void LLScrollbar::setDocSize(S32 size) -{ - if (size != mDocSize) - { - mDocSize = size; - setDocPos(mDocPos); - mDocChanged = true; - - updateThumbRect(); - } -} - -void LLScrollbar::setPageSize( S32 page_size ) -{ - if (page_size != mPageSize) - { - mPageSize = page_size; - setDocPos(mDocPos); - mDocChanged = true; - - updateThumbRect(); - } -} - -bool LLScrollbar::isAtBeginning() const -{ - return mDocPos == 0; -} - -bool LLScrollbar::isAtEnd() const -{ - return mDocPos == getDocPosMax(); -} - - -void LLScrollbar::updateThumbRect() -{ -// llassert( 0 <= mDocSize ); -// llassert( 0 <= mDocPos && mDocPos <= getDocPosMax() ); - - 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 * 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; - - S32 variable_lines = mDocSize - visible_lines; - - if( mOrientation == LLScrollbar::VERTICAL ) - { - 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 = mThickness; - mThumbRect.mBottom = thumb_start - thumb_length; - } - else - { - // Horizontal - 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 = mThickness; - mThumbRect.mRight = thumb_start + thumb_length; - mThumbRect.mBottom = 0; - } -} - -bool LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // Check children first - bool handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL; - if( !handled_by_child ) - { - if( mThumbRect.pointInRect(x,y) ) - { - // Start dragging the thumb - // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture( this ); - mDragStartX = x; - mDragStartY = y; - mOrigRect.mTop = mThumbRect.mTop; - mOrigRect.mBottom = mThumbRect.mBottom; - mOrigRect.mLeft = mThumbRect.mLeft; - mOrigRect.mRight = mThumbRect.mRight; - mLastDelta = 0; - } - else - { - if( - ( (LLScrollbar::VERTICAL == mOrientation) && (mThumbRect.mTop < y) ) || - ( (LLScrollbar::HORIZONTAL == mOrientation) && (x < mThumbRect.mLeft) ) - ) - { - // Page up - pageUp(0); - } - else - if( - ( (LLScrollbar::VERTICAL == mOrientation) && (y < mThumbRect.mBottom) ) || - ( (LLScrollbar::HORIZONTAL == mOrientation) && (mThumbRect.mRight < x) ) - ) - { - // Page down - pageDown(0); - } - } - } - - return true; -} - - -bool LLScrollbar::handleHover(S32 x, S32 y, MASK mask) -{ - // Note: we don't bother sending the event to the children (the arrow buttons) - // because they'll capture the mouse whenever they need hover events. - - bool handled = false; - if( hasMouseCapture() ) - { - S32 height = getRect().getHeight(); - S32 width = getRect().getWidth(); - - if( VERTICAL == mOrientation ) - { -// S32 old_pos = mThumbRect.mTop; - - S32 delta_pixels = y - mDragStartY; - if( mOrigRect.mBottom + delta_pixels < mThickness ) - { - delta_pixels = mThickness - mOrigRect.mBottom - 1; - } - else - if( mOrigRect.mTop + delta_pixels > height - mThickness ) - { - 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 * mThickness; - - - if( delta_pixels != mLastDelta || mDocChanged) - { - // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page). - S32 usable_track_length = thumb_track_length - thumb_length; - if( 0 < usable_track_length ) - { - S32 variable_lines = getDocPosMax(); - S32 pos = mThumbRect.mTop; - 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 - // out of sync (less than a line's worth) to make the thumb feel responsive. - changeLine( new_pos - mDocPos, false ); - } - } - - mLastDelta = delta_pixels; - - } - else - { - // Horizontal -// S32 old_pos = mThumbRect.mLeft; - - S32 delta_pixels = x - mDragStartX; - - if( mOrigRect.mLeft + delta_pixels < mThickness ) - { - delta_pixels = mThickness - mOrigRect.mLeft - 1; - } - else - if( mOrigRect.mRight + delta_pixels > width - mThickness ) - { - 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 * mThickness; - - if( delta_pixels != mLastDelta || mDocChanged) - { - // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page). - S32 usable_track_length = thumb_track_length - thumb_length; - if( 0 < usable_track_length ) - { - S32 variable_lines = getDocPosMax(); - S32 pos = mThumbRect.mLeft; - F32 ratio = F32(pos - mThickness) / usable_track_length; - - S32 new_pos = llclamp( S32(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 - // out of sync (less than a line's worth) to make the thumb feel responsive. - changeLine( new_pos - mDocPos, false ); - } - } - - mLastDelta = delta_pixels; - } - - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - handled = true; - } - else - { - handled = childrenHandleHover( x, y, mask ) != NULL; - } - - // Opaque - if( !handled ) - { - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = true; - } - - mDocChanged = false; - return handled; -} // end handleHover - - -bool LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - bool handled = changeLine( clicks * mStepSize, true ); - return handled; -} - -bool LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks) -{ - bool handled = false; - if (LLScrollbar::HORIZONTAL == mOrientation) - { - handled = changeLine(clicks * mStepSize, true); - } - return handled; -} - -bool LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, - EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string &tooltip_msg) -{ - // enable this to get drag and drop to control scrollbars - //if (!drop) - //{ - // //TODO: refactor this - // S32 variable_lines = getDocPosMax(); - // S32 pos = (VERTICAL == mOrientation) ? y : x; - // S32 thumb_length = (VERTICAL == mOrientation) ? mThumbRect.getHeight() : mThumbRect.getWidth(); - // S32 thumb_track_length = (VERTICAL == mOrientation) ? (getRect().getHeight() - 2 * SCROLLBAR_SIZE) : (getRect().getWidth() - 2 * SCROLLBAR_SIZE); - // S32 usable_track_length = thumb_track_length - thumb_length; - // F32 ratio = (VERTICAL == mOrientation) ? F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length - // : F32(pos - SCROLLBAR_SIZE) / usable_track_length; - // S32 new_pos = (VERTICAL == mOrientation) ? llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines ) - // : llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines ); - // changeLine( new_pos - mDocPos, true ); - //} - //return true; - return false; -} - -bool LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - if( hasMouseCapture() ) - { - gFocusMgr.setMouseCapture( NULL ); - handled = true; - } - else - { - // Opaque, so don't just check children - handled = LLView::handleMouseUp( x, y, mask ); - } - - return handled; -} - -bool LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - // just treat a double click as a second click - return handleMouseDown(x, y, mask); -} - - -void LLScrollbar::reshape(S32 width, S32 height, bool called_from_parent) -{ - if (width == getRect().getWidth() && height == getRect().getHeight()) return; - LLView::reshape( width, height, called_from_parent ); - LLButton* up_button = getChild("Line Up"); - LLButton* down_button = getChild("Line Down"); - - if (mOrientation == VERTICAL) - { - 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(0, getRect().getHeight() - up_button->getRect().getHeight()); - down_button->setOrigin(0, 0); - } - else - { - up_button->reshape(llmin(getRect().getWidth() / 2, mThickness), up_button->getRect().getHeight()); - down_button->reshape(llmin(getRect().getWidth() / 2, mThickness), down_button->getRect().getHeight()); - up_button->setOrigin(0, 0); - down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), 0); - } - updateThumbRect(); -} - - -void LLScrollbar::draw() -{ - if (!getRect().isValid()) return; - - if(mBGVisible) - { - gl_rect_2d(getLocalRect(), mBGColor.get(), true); - } - - S32 local_mouse_x; - S32 local_mouse_y; - LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); - bool other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this; - bool hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y)); - if (hovered) - { - mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLSmoothInterpolation::getInterpolant(0.05f)); - } - else - { - mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f)); - } - - // Draw background and thumb. - if ( ( mOrientation == VERTICAL&&(mThumbImageV.isNull() || mThumbImageH.isNull()) ) - || (mOrientation == HORIZONTAL&&(mTrackImageH.isNull() || mTrackImageV.isNull()) )) - { - 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.get(), true); - - gl_rect_2d(mThumbRect, mThumbColor.get(), true); - - } - else - { - // Thumb - LLRect outline_rect = mThumbRect; - outline_rect.stretch(2); - // Background - - if(mOrientation == HORIZONTAL) - { - mTrackImageH->drawSolid(mThickness //S32 x - , 0 //S32 y - , getRect().getWidth() - 2 * mThickness //S32 width - , getRect().getHeight() //S32 height - , mTrackColor.get()); //const LLColor4& color - - if (gFocusMgr.getKeyboardFocus() == this) - { - mTrackImageH->draw(outline_rect, gFocusMgr.getFocusColor()); - } - - mThumbImageH->draw(mThumbRect, mThumbColor.get()); - if (mCurGlowStrength > 0.01f) - { - gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - mThumbImageH->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - - } - else if(mOrientation == VERTICAL) - { - mTrackImageV->drawSolid( 0 //S32 x - , mThickness //S32 y - , getRect().getWidth() //S32 width - , getRect().getHeight() - 2 * mThickness //S32 height - , mTrackColor.get()); //const LLColor4& color - if (gFocusMgr.getKeyboardFocus() == this) - { - mTrackImageV->draw(outline_rect, gFocusMgr.getFocusColor()); - } - - mThumbImageV->draw(mThumbRect, mThumbColor.get()); - if (mCurGlowStrength > 0.01f) - { - gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - mThumbImageV->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - } - } - - // Draw children - LLView::draw(); -} // end draw - - -bool LLScrollbar::changeLine( S32 delta, bool update_thumb ) -{ - return setDocPos(mDocPos + delta, update_thumb); -} - -void LLScrollbar::setValue(const LLSD& value) -{ - setDocPos((S32) value.asInteger()); -} - - -bool LLScrollbar::handleKeyHere(KEY key, MASK mask) -{ - if (getDocPosMax() == 0 && !getVisible()) - { - return false; - } - - bool handled = false; - - switch( key ) - { - case KEY_HOME: - setDocPos( 0 ); - handled = true; - break; - - case KEY_END: - setDocPos( getDocPosMax() ); - handled = true; - break; - - case KEY_DOWN: - setDocPos( getDocPos() + mStepSize ); - handled = true; - break; - - case KEY_UP: - setDocPos( getDocPos() - mStepSize ); - handled = true; - break; - - case KEY_PAGE_DOWN: - pageDown(1); - break; - - case KEY_PAGE_UP: - pageUp(1); - break; - } - - return handled; -} - -void LLScrollbar::pageUp(S32 overlap) -{ - if (mDocSize > mPageSize) - { - changeLine( -(mPageSize - overlap), true ); - } -} - -void LLScrollbar::pageDown(S32 overlap) -{ - if (mDocSize > mPageSize) - { - changeLine( mPageSize - overlap, true ); - } -} - -void LLScrollbar::onLineUpBtnPressed( const LLSD& data ) -{ - changeLine( -mStepSize, true ); -} - -void LLScrollbar::onLineDownBtnPressed( const LLSD& data ) -{ - changeLine( mStepSize, true ); -} - -void LLScrollbar::setThickness(S32 thickness) -{ - mThickness = thickness < 0 ? LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize") : thickness; -} +/** + * @file llscrollbar.cpp + * @brief Scrollbar UI widget + * + * $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 "linden_common.h" + +#include "llscrollbar.h" + +#include "llmath.h" +#include "lltimer.h" +#include "v3color.h" + +#include "llbutton.h" +#include "llcriticaldamp.h" +#include "llkeyboard.h" +#include "llui.h" +#include "llfocusmgr.h" +#include "llwindow.h" +#include "llcontrol.h" +#include "llrender.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register register_scrollbar("scroll_bar"); + +LLScrollbar::Params::Params() +: orientation ("orientation", HORIZONTAL), + doc_size ("doc_size", 0), + doc_pos ("doc_pos", 0), + page_size ("page_size", 0), + step_size ("step_size", 1), + thumb_image_vertical("thumb_image_vertical"), + thumb_image_horizontal("thumb_image_horizontal"), + track_image_vertical("track_image_vertical"), + track_image_horizontal("track_image_horizontal"), + track_color("track_color"), + thumb_color("thumb_color"), + thickness("thickness"), + up_button("up_button"), + down_button("down_button"), + left_button("left_button"), + right_button("right_button"), + bg_visible("bg_visible", false), + bg_color("bg_color", LLColor4::black) +{} + +LLScrollbar::LLScrollbar(const Params & p) +: LLUICtrl(p), + mChangeCallback( p.change_callback() ), + mOrientation( p.orientation ), + mDocSize( p.doc_size ), + mDocPos( p.doc_pos ), + mPageSize( p.page_size ), + mStepSize( p.step_size ), + mDocChanged(false), + mDragStartX( 0 ), + mDragStartY( 0 ), + mHoverGlowStrength(0.15f), + mCurGlowStrength(0.f), + mTrackColor( p.track_color() ), + mThumbColor ( p.thumb_color() ), + mThumbImageV(p.thumb_image_vertical), + mThumbImageH(p.thumb_image_horizontal), + mTrackImageV(p.track_image_vertical), + mTrackImageH(p.track_image_horizontal), + mThickness(p.thickness.isProvided() ? p.thickness : LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize")), + mBGVisible(p.bg_visible), + mBGColor(p.bg_color) +{ + updateThumbRect(); + + // Page up and page down buttons + LLRect line_up_rect; + LLRect line_down_rect; + + if( VERTICAL == mOrientation ) + { + line_up_rect.setLeftTopAndSize( 0, getRect().getHeight(), mThickness, mThickness ); + line_down_rect.setOriginAndSize( 0, 0, mThickness, mThickness ); + } + else // HORIZONTAL + { + line_up_rect.setOriginAndSize( 0, 0, mThickness, mThickness ); + line_down_rect.setOriginAndSize( getRect().getWidth() - mThickness, 0, mThickness, mThickness ); + } + + LLButton::Params up_btn(mOrientation == VERTICAL ? p.up_button : p.left_button); + up_btn.name(std::string("Line Up")); + up_btn.rect(line_up_rect); + up_btn.click_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2)); + up_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2)); + up_btn.tab_stop(false); + up_btn.follows.flags = (mOrientation == VERTICAL ? (FOLLOWS_RIGHT | FOLLOWS_TOP) : (FOLLOWS_LEFT | FOLLOWS_BOTTOM)); + + addChild(LLUICtrlFactory::create(up_btn)); + + LLButton::Params down_btn(mOrientation == VERTICAL ? p.down_button : p.right_button); + down_btn.name(std::string("Line Down")); + down_btn.rect(line_down_rect); + down_btn.follows.flags(FOLLOWS_RIGHT|FOLLOWS_BOTTOM); + down_btn.click_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2)); + down_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2)); + down_btn.tab_stop(false); + + addChild(LLUICtrlFactory::create(down_btn)); +} + + +LLScrollbar::~LLScrollbar() +{ + // Children buttons killed by parent class +} + +void LLScrollbar::setDocParams( S32 size, S32 pos ) +{ + mDocSize = size; + setDocPos(pos); + mDocChanged = true; + + updateThumbRect(); +} + +// returns true if document position really changed +bool LLScrollbar::setDocPos(S32 pos, bool update_thumb) +{ + pos = llclamp(pos, 0, getDocPosMax()); + if (pos != mDocPos) + { + mDocPos = pos; + mDocChanged = true; + + if( mChangeCallback ) + { + mChangeCallback( mDocPos, this ); + } + + if( update_thumb ) + { + updateThumbRect(); + } + return true; + } + return false; +} + +void LLScrollbar::setDocSize(S32 size) +{ + if (size != mDocSize) + { + mDocSize = size; + setDocPos(mDocPos); + mDocChanged = true; + + updateThumbRect(); + } +} + +void LLScrollbar::setPageSize( S32 page_size ) +{ + if (page_size != mPageSize) + { + mPageSize = page_size; + setDocPos(mDocPos); + mDocChanged = true; + + updateThumbRect(); + } +} + +bool LLScrollbar::isAtBeginning() const +{ + return mDocPos == 0; +} + +bool LLScrollbar::isAtEnd() const +{ + return mDocPos == getDocPosMax(); +} + + +void LLScrollbar::updateThumbRect() +{ +// llassert( 0 <= mDocSize ); +// llassert( 0 <= mDocPos && mDocPos <= getDocPosMax() ); + + 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 * 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; + + S32 variable_lines = mDocSize - visible_lines; + + if( mOrientation == LLScrollbar::VERTICAL ) + { + 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 = mThickness; + mThumbRect.mBottom = thumb_start - thumb_length; + } + else + { + // Horizontal + 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 = mThickness; + mThumbRect.mRight = thumb_start + thumb_length; + mThumbRect.mBottom = 0; + } +} + +bool LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // Check children first + bool handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + if( !handled_by_child ) + { + if( mThumbRect.pointInRect(x,y) ) + { + // Start dragging the thumb + // No handler needed for focus lost since this clas has no state that depends on it. + gFocusMgr.setMouseCapture( this ); + mDragStartX = x; + mDragStartY = y; + mOrigRect.mTop = mThumbRect.mTop; + mOrigRect.mBottom = mThumbRect.mBottom; + mOrigRect.mLeft = mThumbRect.mLeft; + mOrigRect.mRight = mThumbRect.mRight; + mLastDelta = 0; + } + else + { + if( + ( (LLScrollbar::VERTICAL == mOrientation) && (mThumbRect.mTop < y) ) || + ( (LLScrollbar::HORIZONTAL == mOrientation) && (x < mThumbRect.mLeft) ) + ) + { + // Page up + pageUp(0); + } + else + if( + ( (LLScrollbar::VERTICAL == mOrientation) && (y < mThumbRect.mBottom) ) || + ( (LLScrollbar::HORIZONTAL == mOrientation) && (mThumbRect.mRight < x) ) + ) + { + // Page down + pageDown(0); + } + } + } + + return true; +} + + +bool LLScrollbar::handleHover(S32 x, S32 y, MASK mask) +{ + // Note: we don't bother sending the event to the children (the arrow buttons) + // because they'll capture the mouse whenever they need hover events. + + bool handled = false; + if( hasMouseCapture() ) + { + S32 height = getRect().getHeight(); + S32 width = getRect().getWidth(); + + if( VERTICAL == mOrientation ) + { +// S32 old_pos = mThumbRect.mTop; + + S32 delta_pixels = y - mDragStartY; + if( mOrigRect.mBottom + delta_pixels < mThickness ) + { + delta_pixels = mThickness - mOrigRect.mBottom - 1; + } + else + if( mOrigRect.mTop + delta_pixels > height - mThickness ) + { + 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 * mThickness; + + + if( delta_pixels != mLastDelta || mDocChanged) + { + // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page). + S32 usable_track_length = thumb_track_length - thumb_length; + if( 0 < usable_track_length ) + { + S32 variable_lines = getDocPosMax(); + S32 pos = mThumbRect.mTop; + 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 + // out of sync (less than a line's worth) to make the thumb feel responsive. + changeLine( new_pos - mDocPos, false ); + } + } + + mLastDelta = delta_pixels; + + } + else + { + // Horizontal +// S32 old_pos = mThumbRect.mLeft; + + S32 delta_pixels = x - mDragStartX; + + if( mOrigRect.mLeft + delta_pixels < mThickness ) + { + delta_pixels = mThickness - mOrigRect.mLeft - 1; + } + else + if( mOrigRect.mRight + delta_pixels > width - mThickness ) + { + 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 * mThickness; + + if( delta_pixels != mLastDelta || mDocChanged) + { + // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page). + S32 usable_track_length = thumb_track_length - thumb_length; + if( 0 < usable_track_length ) + { + S32 variable_lines = getDocPosMax(); + S32 pos = mThumbRect.mLeft; + F32 ratio = F32(pos - mThickness) / usable_track_length; + + S32 new_pos = llclamp( S32(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 + // out of sync (less than a line's worth) to make the thumb feel responsive. + changeLine( new_pos - mDocPos, false ); + } + } + + mLastDelta = delta_pixels; + } + + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; + handled = true; + } + else + { + handled = childrenHandleHover( x, y, mask ) != NULL; + } + + // Opaque + if( !handled ) + { + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; + handled = true; + } + + mDocChanged = false; + return handled; +} // end handleHover + + +bool LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + bool handled = changeLine( clicks * mStepSize, true ); + return handled; +} + +bool LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks) +{ + bool handled = false; + if (LLScrollbar::HORIZONTAL == mOrientation) + { + handled = changeLine(clicks * mStepSize, true); + } + return handled; +} + +bool LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, + EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string &tooltip_msg) +{ + // enable this to get drag and drop to control scrollbars + //if (!drop) + //{ + // //TODO: refactor this + // S32 variable_lines = getDocPosMax(); + // S32 pos = (VERTICAL == mOrientation) ? y : x; + // S32 thumb_length = (VERTICAL == mOrientation) ? mThumbRect.getHeight() : mThumbRect.getWidth(); + // S32 thumb_track_length = (VERTICAL == mOrientation) ? (getRect().getHeight() - 2 * SCROLLBAR_SIZE) : (getRect().getWidth() - 2 * SCROLLBAR_SIZE); + // S32 usable_track_length = thumb_track_length - thumb_length; + // F32 ratio = (VERTICAL == mOrientation) ? F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length + // : F32(pos - SCROLLBAR_SIZE) / usable_track_length; + // S32 new_pos = (VERTICAL == mOrientation) ? llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines ) + // : llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines ); + // changeLine( new_pos - mDocPos, true ); + //} + //return true; + return false; +} + +bool LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + if( hasMouseCapture() ) + { + gFocusMgr.setMouseCapture( NULL ); + handled = true; + } + else + { + // Opaque, so don't just check children + handled = LLView::handleMouseUp( x, y, mask ); + } + + return handled; +} + +bool LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + // just treat a double click as a second click + return handleMouseDown(x, y, mask); +} + + +void LLScrollbar::reshape(S32 width, S32 height, bool called_from_parent) +{ + if (width == getRect().getWidth() && height == getRect().getHeight()) return; + LLView::reshape( width, height, called_from_parent ); + LLButton* up_button = getChild("Line Up"); + LLButton* down_button = getChild("Line Down"); + + if (mOrientation == VERTICAL) + { + 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(0, getRect().getHeight() - up_button->getRect().getHeight()); + down_button->setOrigin(0, 0); + } + else + { + up_button->reshape(llmin(getRect().getWidth() / 2, mThickness), up_button->getRect().getHeight()); + down_button->reshape(llmin(getRect().getWidth() / 2, mThickness), down_button->getRect().getHeight()); + up_button->setOrigin(0, 0); + down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), 0); + } + updateThumbRect(); +} + + +void LLScrollbar::draw() +{ + if (!getRect().isValid()) return; + + if(mBGVisible) + { + gl_rect_2d(getLocalRect(), mBGColor.get(), true); + } + + S32 local_mouse_x; + S32 local_mouse_y; + LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); + bool other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this; + bool hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y)); + if (hovered) + { + mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLSmoothInterpolation::getInterpolant(0.05f)); + } + else + { + mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f)); + } + + // Draw background and thumb. + if ( ( mOrientation == VERTICAL&&(mThumbImageV.isNull() || mThumbImageH.isNull()) ) + || (mOrientation == HORIZONTAL&&(mTrackImageH.isNull() || mTrackImageV.isNull()) )) + { + 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.get(), true); + + gl_rect_2d(mThumbRect, mThumbColor.get(), true); + + } + else + { + // Thumb + LLRect outline_rect = mThumbRect; + outline_rect.stretch(2); + // Background + + if(mOrientation == HORIZONTAL) + { + mTrackImageH->drawSolid(mThickness //S32 x + , 0 //S32 y + , getRect().getWidth() - 2 * mThickness //S32 width + , getRect().getHeight() //S32 height + , mTrackColor.get()); //const LLColor4& color + + if (gFocusMgr.getKeyboardFocus() == this) + { + mTrackImageH->draw(outline_rect, gFocusMgr.getFocusColor()); + } + + mThumbImageH->draw(mThumbRect, mThumbColor.get()); + if (mCurGlowStrength > 0.01f) + { + gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); + mThumbImageH->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + + } + else if(mOrientation == VERTICAL) + { + mTrackImageV->drawSolid( 0 //S32 x + , mThickness //S32 y + , getRect().getWidth() //S32 width + , getRect().getHeight() - 2 * mThickness //S32 height + , mTrackColor.get()); //const LLColor4& color + if (gFocusMgr.getKeyboardFocus() == this) + { + mTrackImageV->draw(outline_rect, gFocusMgr.getFocusColor()); + } + + mThumbImageV->draw(mThumbRect, mThumbColor.get()); + if (mCurGlowStrength > 0.01f) + { + gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); + mThumbImageV->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + } + } + + // Draw children + LLView::draw(); +} // end draw + + +bool LLScrollbar::changeLine( S32 delta, bool update_thumb ) +{ + return setDocPos(mDocPos + delta, update_thumb); +} + +void LLScrollbar::setValue(const LLSD& value) +{ + setDocPos((S32) value.asInteger()); +} + + +bool LLScrollbar::handleKeyHere(KEY key, MASK mask) +{ + if (getDocPosMax() == 0 && !getVisible()) + { + return false; + } + + bool handled = false; + + switch( key ) + { + case KEY_HOME: + setDocPos( 0 ); + handled = true; + break; + + case KEY_END: + setDocPos( getDocPosMax() ); + handled = true; + break; + + case KEY_DOWN: + setDocPos( getDocPos() + mStepSize ); + handled = true; + break; + + case KEY_UP: + setDocPos( getDocPos() - mStepSize ); + handled = true; + break; + + case KEY_PAGE_DOWN: + pageDown(1); + break; + + case KEY_PAGE_UP: + pageUp(1); + break; + } + + return handled; +} + +void LLScrollbar::pageUp(S32 overlap) +{ + if (mDocSize > mPageSize) + { + changeLine( -(mPageSize - overlap), true ); + } +} + +void LLScrollbar::pageDown(S32 overlap) +{ + if (mDocSize > mPageSize) + { + changeLine( mPageSize - overlap, true ); + } +} + +void LLScrollbar::onLineUpBtnPressed( const LLSD& data ) +{ + changeLine( -mStepSize, true ); +} + +void LLScrollbar::onLineDownBtnPressed( const LLSD& data ) +{ + changeLine( mStepSize, true ); +} + +void LLScrollbar::setThickness(S32 thickness) +{ + mThickness = thickness < 0 ? LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize") : thickness; +} diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index b93c988b37..7b935aa51b 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -1,167 +1,167 @@ -/** - * @file llscrollbar.h - * @brief Scrollbar UI widget - * - * $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$ - */ - -#ifndef LL_SCROLLBAR_H -#define LL_SCROLLBAR_H - -#include "stdtypes.h" -#include "lluictrl.h" -#include "v4color.h" -#include "llbutton.h" - -// -// Classes -// -class LLScrollbar -: public LLUICtrl -{ -public: - - typedef boost::function callback_t; - struct Params - : public LLInitParam::Block - { - Mandatory orientation; - Mandatory doc_size; - Mandatory doc_pos; - Mandatory page_size; - - Optional change_callback; - Optional step_size; - Optional thickness; - - Optional thumb_image_vertical, - thumb_image_horizontal, - track_image_horizontal, - track_image_vertical; - - Optional bg_visible; - - Optional track_color, - thumb_color, - bg_color; - - Optional up_button; - Optional down_button; - Optional left_button; - Optional right_button; - - Params(); - }; - -protected: - LLScrollbar (const Params & p); - friend class LLUICtrlFactory; - -public: - virtual ~LLScrollbar(); - - virtual void setValue(const LLSD& value); - - // Overrides from LLView - 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 handleDoubleClick(S32 x, S32 y, MASK mask); - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); - virtual bool handleScrollHWheel(S32 x, S32 y, S32 clicks); - 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 reshape(S32 width, S32 height, bool called_from_parent = true); - - virtual void draw(); - - // How long the "document" is. - void setDocSize( S32 size ); - S32 getDocSize() const { return mDocSize; } - - // How many "lines" the "document" has scrolled. - // 0 <= DocPos <= DocSize - DocVisibile - bool setDocPos( S32 pos, bool update_thumb = true ); - S32 getDocPos() const { return mDocPos; } - - bool isAtBeginning() const; - bool isAtEnd() const; - - // Setting both at once. - void setDocParams( S32 size, S32 pos ); - - // How many "lines" of the "document" is can appear on a page. - void setPageSize( S32 page_size ); - S32 getPageSize() const { return mPageSize; } - - // The farthest the document can be scrolled (top of the last page). - S32 getDocPosMax() const { return llmax( 0, mDocSize - mPageSize); } - - void pageUp(S32 overlap); - void pageDown(S32 overlap); - - void onLineUpBtnPressed(const LLSD& data); - void onLineDownBtnPressed(const LLSD& data); - - S32 getThickness() const { return mThickness; } - void setThickness(S32 thickness); - -private: - void updateThumbRect(); - bool changeLine(S32 delta, bool update_thumb ); - - callback_t mChangeCallback; - - const EOrientation mOrientation; - S32 mDocSize; // Size of the document that the scrollbar is modeling. Units depend on the user. 0 <= mDocSize. - S32 mDocPos; // Position within the doc that the scrollbar is modeling, in "lines" (user size) - S32 mPageSize; // Maximum number of lines that can be seen at one time. - S32 mStepSize; - bool mDocChanged; - - LLRect mThumbRect; - S32 mDragStartX; - S32 mDragStartY; - F32 mHoverGlowStrength; - F32 mCurGlowStrength; - - LLRect mOrigRect; - S32 mLastDelta; - - LLUIColor mTrackColor; - LLUIColor mThumbColor; - LLUIColor mBGColor; - - bool mBGVisible; - - LLUIImagePtr mThumbImageV; - LLUIImagePtr mThumbImageH; - LLUIImagePtr mTrackImageV; - LLUIImagePtr mTrackImageH; - - S32 mThickness; -}; - - -#endif // LL_SCROLLBAR_H +/** + * @file llscrollbar.h + * @brief Scrollbar UI widget + * + * $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$ + */ + +#ifndef LL_SCROLLBAR_H +#define LL_SCROLLBAR_H + +#include "stdtypes.h" +#include "lluictrl.h" +#include "v4color.h" +#include "llbutton.h" + +// +// Classes +// +class LLScrollbar +: public LLUICtrl +{ +public: + + typedef boost::function callback_t; + struct Params + : public LLInitParam::Block + { + Mandatory orientation; + Mandatory doc_size; + Mandatory doc_pos; + Mandatory page_size; + + Optional change_callback; + Optional step_size; + Optional thickness; + + Optional thumb_image_vertical, + thumb_image_horizontal, + track_image_horizontal, + track_image_vertical; + + Optional bg_visible; + + Optional track_color, + thumb_color, + bg_color; + + Optional up_button; + Optional down_button; + Optional left_button; + Optional right_button; + + Params(); + }; + +protected: + LLScrollbar (const Params & p); + friend class LLUICtrlFactory; + +public: + virtual ~LLScrollbar(); + + virtual void setValue(const LLSD& value); + + // Overrides from LLView + 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 handleDoubleClick(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); + virtual bool handleScrollHWheel(S32 x, S32 y, S32 clicks); + 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 reshape(S32 width, S32 height, bool called_from_parent = true); + + virtual void draw(); + + // How long the "document" is. + void setDocSize( S32 size ); + S32 getDocSize() const { return mDocSize; } + + // How many "lines" the "document" has scrolled. + // 0 <= DocPos <= DocSize - DocVisibile + bool setDocPos( S32 pos, bool update_thumb = true ); + S32 getDocPos() const { return mDocPos; } + + bool isAtBeginning() const; + bool isAtEnd() const; + + // Setting both at once. + void setDocParams( S32 size, S32 pos ); + + // How many "lines" of the "document" is can appear on a page. + void setPageSize( S32 page_size ); + S32 getPageSize() const { return mPageSize; } + + // The farthest the document can be scrolled (top of the last page). + S32 getDocPosMax() const { return llmax( 0, mDocSize - mPageSize); } + + void pageUp(S32 overlap); + void pageDown(S32 overlap); + + void onLineUpBtnPressed(const LLSD& data); + void onLineDownBtnPressed(const LLSD& data); + + S32 getThickness() const { return mThickness; } + void setThickness(S32 thickness); + +private: + void updateThumbRect(); + bool changeLine(S32 delta, bool update_thumb ); + + callback_t mChangeCallback; + + const EOrientation mOrientation; + S32 mDocSize; // Size of the document that the scrollbar is modeling. Units depend on the user. 0 <= mDocSize. + S32 mDocPos; // Position within the doc that the scrollbar is modeling, in "lines" (user size) + S32 mPageSize; // Maximum number of lines that can be seen at one time. + S32 mStepSize; + bool mDocChanged; + + LLRect mThumbRect; + S32 mDragStartX; + S32 mDragStartY; + F32 mHoverGlowStrength; + F32 mCurGlowStrength; + + LLRect mOrigRect; + S32 mLastDelta; + + LLUIColor mTrackColor; + LLUIColor mThumbColor; + LLUIColor mBGColor; + + bool mBGVisible; + + LLUIImagePtr mThumbImageV; + LLUIImagePtr mThumbImageH; + LLUIImagePtr mTrackImageV; + LLUIImagePtr mTrackImageH; + + S32 mThickness; +}; + + +#endif // LL_SCROLLBAR_H diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 1e99d408cc..df99c4f636 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -1,802 +1,802 @@ -/** - * @file llscrollcontainer.cpp - * @brief LLScrollContainer base class - * - * $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 "linden_common.h" - -#include "llscrollcontainer.h" - -#include "llrender.h" -#include "llcontainerview.h" -#include "lllocalcliprect.h" -// #include "llfolderview.h" -#include "llscrollingpanellist.h" -#include "llscrollbar.h" -#include "llui.h" -#include "llkeyboard.h" -#include "llviewborder.h" -#include "llfocusmgr.h" -#include "llframetimer.h" -#include "lluictrlfactory.h" -#include "llpanel.h" -#include "llfontgl.h" - -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- - -static const S32 VERTICAL_MULTIPLE = 16; -static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; - -///---------------------------------------------------------------------------- -/// Class LLScrollContainer -///---------------------------------------------------------------------------- - -static LLDefaultChildRegistry::Register r("scroll_container"); - -#include "llscrollingpanellist.h" -#include "llcontainerview.h" -#include "llpanel.h" - -static ScrollContainerRegistry::Register r1("scrolling_panel_list"); -static ScrollContainerRegistry::Register r2("container_view"); -static ScrollContainerRegistry::Register r3("panel", &LLPanel::fromXML); - -LLScrollContainer::Params::Params() -: is_opaque("opaque"), - bg_color("color"), - border_visible("border_visible"), - hide_scrollbar("hide_scrollbar"), - ignore_arrow_keys("ignore_arrow_keys"), - min_auto_scroll_rate("min_auto_scroll_rate", 100), - max_auto_scroll_rate("max_auto_scroll_rate", 1000), - max_auto_scroll_zone("max_auto_scroll_zone", 16), - reserve_scroll_corner("reserve_scroll_corner", false), - size("size", -1) -{} - - -// Default constructor -LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) -: LLUICtrl(p), - mAutoScrolling( false ), - mAutoScrollRate( 0.f ), - mBackgroundColor(p.bg_color()), - mIsOpaque(p.is_opaque), - mHideScrollbar(p.hide_scrollbar), - mIgnoreArrowKeys(p.ignore_arrow_keys), - mReserveScrollCorner(p.reserve_scroll_corner), - mMinAutoScrollRate(p.min_auto_scroll_rate), - mMaxAutoScrollRate(p.max_auto_scroll_rate), - mMaxAutoScrollZone(p.max_auto_scroll_zone), - mScrolledView(NULL), - mSize(p.size) -{ - static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); - S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); - - LLRect border_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - LLViewBorder::Params params; - params.name("scroll border"); - params.rect(border_rect); - params.visible(p.border_visible); - params.bevel_style(LLViewBorder::BEVEL_IN); - mBorder = LLUICtrlFactory::create (params); - LLView::addChild( mBorder ); - - mInnerRect = getLocalRect(); - mInnerRect.stretch( -getBorderWidth() ); - - LLRect vertical_scroll_rect = mInnerRect; - vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size; - LLScrollbar::Params sbparams; - sbparams.name("scrollable vertical"); - sbparams.rect(vertical_scroll_rect); - sbparams.orientation(LLScrollbar::VERTICAL); - sbparams.doc_size(mInnerRect.getHeight()); - sbparams.doc_pos(0); - sbparams.page_size(mInnerRect.getHeight()); - sbparams.step_size(VERTICAL_MULTIPLE); - sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); - sbparams.visible(false); - sbparams.change_callback(p.scroll_callback); - mScrollbar[VERTICAL] = LLUICtrlFactory::create (sbparams); - LLView::addChild( mScrollbar[VERTICAL] ); - - LLRect horizontal_scroll_rect; - horizontal_scroll_rect.mTop = scrollbar_size; - horizontal_scroll_rect.mRight = mInnerRect.getWidth(); - sbparams.name("scrollable horizontal"); - sbparams.rect(horizontal_scroll_rect); - sbparams.orientation(LLScrollbar::HORIZONTAL); - sbparams.doc_size(mInnerRect.getWidth()); - sbparams.doc_pos(0); - sbparams.page_size(mInnerRect.getWidth()); - sbparams.step_size(VERTICAL_MULTIPLE); - sbparams.visible(false); - sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); - sbparams.change_callback(p.scroll_callback); - mScrollbar[HORIZONTAL] = LLUICtrlFactory::create (sbparams); - LLView::addChild( mScrollbar[HORIZONTAL] ); -} - -// Destroys the object -LLScrollContainer::~LLScrollContainer( void ) -{ - // mScrolledView and mScrollbar are child views, so the LLView - // destructor takes care of memory deallocation. - for( S32 i = 0; i < ORIENTATION_COUNT; i++ ) - { - mScrollbar[i] = NULL; - } - mScrolledView = NULL; -} - -// internal scrollbar handlers -// virtual -void LLScrollContainer::scrollHorizontal( S32 new_pos ) -{ - if( mScrolledView ) - { - LLRect doc_rect = mScrolledView->getRect(); - S32 old_pos = -(doc_rect.mLeft - mInnerRect.mLeft); - mScrolledView->translate( -(new_pos - old_pos), 0 ); - } -} - -// virtual -void LLScrollContainer::scrollVertical( S32 new_pos ) -{ - if( mScrolledView ) - { - LLRect doc_rect = mScrolledView->getRect(); - S32 old_pos = doc_rect.mTop - mInnerRect.mTop; - mScrolledView->translate( 0, new_pos - old_pos ); - } -} - -// LLView functionality -void LLScrollContainer::reshape(S32 width, S32 height, - bool called_from_parent) -{ - LLUICtrl::reshape( width, height, called_from_parent ); - - mInnerRect = getLocalRect(); - mInnerRect.stretch( -getBorderWidth() ); - - if (mScrolledView) - { - const LLRect& scrolled_rect = mScrolledView->getRect(); - - 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 ); - - mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); - mScrollbar[VERTICAL]->setPageSize( visible_height ); - - mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); - mScrollbar[HORIZONTAL]->setPageSize( visible_width ); - updateScroll(); - } -} - -// virtual -bool LLScrollContainer::handleKeyHere(KEY key, MASK mask) -{ - if (mIgnoreArrowKeys) - { - switch(key) - { - case KEY_LEFT: - case KEY_RIGHT: - case KEY_UP: - case KEY_DOWN: - case KEY_PAGE_UP: - case KEY_PAGE_DOWN: - case KEY_HOME: - case KEY_END: - return false; - default: - break; - } - } - - // 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 < ORIENTATION_COUNT; i++ ) - { - if( mScrollbar[i]->handleKeyHere(key, mask) ) - { - updateScroll(); - return true; - } - } - - return false; -} - -bool LLScrollContainer::handleUnicodeCharHere(llwchar uni_char) -{ - if (mScrolledView && mScrolledView->handleUnicodeCharHere(uni_char)) - { - return true; - } - return false; -} - -bool LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) -{ - // 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 (vertical->handleScrollWheel( 0, 0, clicks ) ) - { - updateScroll(); - } - // Always eat the event - return true; - } - - LLScrollbar* horizontal = mScrollbar[HORIZONTAL]; - // Test enablement and visibility for consistency with - // LLView::childrenHandleScrollWheel(). - if (horizontal->getVisible() - && horizontal->getEnabled() - && horizontal->handleScrollWheel( 0, 0, clicks ) ) - { - updateScroll(); - return true; - } - return false; -} - -bool LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks) -{ - if (LLUICtrl::handleScrollHWheel(x,y,clicks)) - { - return true; - } - - LLScrollbar* horizontal = mScrollbar[HORIZONTAL]; - if (horizontal->getVisible() - && horizontal->getEnabled() - && horizontal->handleScrollHWheel( 0, 0, clicks ) ) - { - updateScroll(); - return true; - } - - return false; -} - -bool LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, - bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - // Scroll folder view if needed. Never accepts a drag or drop. - *accept = ACCEPT_NO; - bool handled = autoScroll(x, y); - - if( !handled ) - { - handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, - cargo_data, accept, tooltip_msg) != NULL; - } - - return true; -} - -bool LLScrollContainer::canAutoScroll(S32 x, S32 y) -{ - if (mAutoScrolling) - { - return true; // already scrolling - } - return autoScroll(x, y, false); -} - -bool LLScrollContainer::autoScroll(S32 x, S32 y) -{ - return autoScroll(x, y, true); -} - -bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll) -{ - static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); - S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); - - 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 ); - // Note: Will also include scrollers as scroll zones, so opposite - // scroll zones might have different size due to visible scrollers - 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 = ll_round(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, (S32)mMaxAutoScrollZone); - S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, (S32)mMaxAutoScrollZone); - - 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) ) - { - if (do_scroll) - { - 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()) ) - { - if (do_scroll) - { - 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()) ) - { - if (do_scroll) - { - 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) ) - { - if (do_scroll) - { - mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed); - mAutoScrolling = true; - } - scrolling = true; - } - } - } - return scrolling; -} - -void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const -{ - const LLRect& doc_rect = getScrolledViewRect(); - static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); - S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); - - S32 doc_width = doc_rect.getWidth(); - S32 doc_height = doc_rect.getHeight(); - - S32 border_width = getBorderWidth(); - *visible_width = getRect().getWidth() - 2 * border_width; - *visible_height = getRect().getHeight() - 2 * border_width; - - *show_v_scrollbar = false; - *show_h_scrollbar = false; - - if (!mHideScrollbar) - { - // Note: 1 pixel change can happen on final animation and should not trigger - // the display of sliders. - if ((doc_height - *visible_height) > 1) - { - *show_v_scrollbar = true; - *visible_width -= scrollbar_size; - } - if ((doc_width - *visible_width) > 1) - { - *show_h_scrollbar = true; - *visible_height -= scrollbar_size; - // Note: Do *not* recompute *show_v_scrollbar here because with - // The view inside the scroll container should not be extended - // to container's full height to ensure the correct computation - // of *show_v_scrollbar after subtracting horizontal scrollbar_size. - - if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) ) - { - *show_v_scrollbar = true; - *visible_width -= scrollbar_size; - } - } - } -} - - -void LLScrollContainer::draw() -{ - static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); - S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); - - if (mAutoScrolling) - { - // add acceleration to autoscroll - mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), mMaxAutoScrollRate); - } - else - { - // reset to minimum for next time - mAutoScrollRate = mMinAutoScrollRate; - } - // 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 (!hasFocus() - && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture())) - { - focusFirstItem(); - } - - if (getRect().isValid()) - { - // Draw background - if( mIsOpaque ) - { - F32 alpha = getCurrentTransparency(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha); - } - - // 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. - { - 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) ); - } - - // 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 ) - { - sDepth++; - } - if( (viewp != mScrolledView) && viewp->getVisible() ) - { - drawChild(viewp); - } - if( sDebugRects ) - { - sDepth--; - } - } - } -} // end draw - -bool LLScrollContainer::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 LLScrollContainer::updateScroll() -{ - if (!getVisible() || !mScrolledView) - { - return; - } - static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); - S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); - - LLRect doc_rect = mScrolledView->getRect(); - S32 doc_width = doc_rect.getWidth(); - S32 doc_height = doc_rect.getHeight(); - 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 ); - - S32 border_width = getBorderWidth(); - if( show_v_scrollbar ) - { - if( doc_rect.mTop < getRect().getHeight() - border_width ) - { - mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop ); - } - - scrollVertical( mScrollbar[VERTICAL]->getDocPos() ); - mScrollbar[VERTICAL]->setVisible( true ); - - S32 v_scrollbar_height = visible_height; - if( !show_h_scrollbar && mReserveScrollCorner ) - { - v_scrollbar_height -= scrollbar_size; - } - 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; - } - LLRect r = mScrollbar[VERTICAL]->getRect(); - r.translate( 0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset ); - mScrollbar[VERTICAL]->setRect( r ); - } - else - { - mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop ); - - mScrollbar[VERTICAL]->setVisible( false ); - mScrollbar[VERTICAL]->setDocPos( 0 ); - } - - if( show_h_scrollbar ) - { - if( doc_rect.mLeft > border_width ) - { - mScrolledView->translate( border_width - doc_rect.mLeft, 0 ); - mScrollbar[HORIZONTAL]->setDocPos( 0 ); - } - else - { - scrollHorizontal( mScrollbar[HORIZONTAL]->getDocPos() ); - } - - mScrollbar[HORIZONTAL]->setVisible( true ); - S32 h_scrollbar_width = visible_width; - if( !show_v_scrollbar && mReserveScrollCorner ) - { - h_scrollbar_width -= scrollbar_size; - } - mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, scrollbar_size, true ); - } - else - { - mScrolledView->translate( border_width - doc_rect.mLeft, 0 ); - - mScrollbar[HORIZONTAL]->setVisible( false ); - mScrollbar[HORIZONTAL]->setDocPos( 0 ); - } - - mScrollbar[HORIZONTAL]->setDocSize( doc_width ); - mScrollbar[HORIZONTAL]->setPageSize( visible_width ); - - mScrollbar[VERTICAL]->setDocSize( doc_height ); - mScrollbar[VERTICAL]->setPageSize( visible_height ); -} // end updateScroll - -void LLScrollContainer::setBorderVisible(bool b) -{ - mBorder->setVisible( b ); - // Recompute inner rect, as border visibility changes it - mInnerRect = getLocalRect(); - mInnerRect.stretch( -getBorderWidth() ); -} - -LLRect LLScrollContainer::getVisibleContentRect() -{ - updateScroll(); - LLRect visible_rect = getContentWindowRect(); - LLRect contents_rect = mScrolledView->getRect(); - visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom); - return visible_rect; -} - -LLRect LLScrollContainer::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 LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& constraint) -{ - if (!mScrolledView) - { - LL_WARNS() << "LLScrollContainer::scrollToShowRect with no view!" << LL_ENDL; - return; - } - - LLRect content_window_rect = getContentWindowRect(); - // get document rect - LLRect scrolled_rect = mScrolledView->getRect(); - - // 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()); - - // 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); - - // 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( content_window_rect.getHeight() ); - mScrollbar[VERTICAL]->setDocPos( vert_pos ); - - S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(), - allowable_scroll_rect.mLeft, - allowable_scroll_rect.mRight); - - mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); - 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 LLScrollContainer::pageUp(S32 overlap) -{ - mScrollbar[VERTICAL]->pageUp(overlap); - updateScroll(); -} - -void LLScrollContainer::pageDown(S32 overlap) -{ - mScrollbar[VERTICAL]->pageDown(overlap); - updateScroll(); -} - -void LLScrollContainer::goToTop() -{ - mScrollbar[VERTICAL]->setDocPos(0); - updateScroll(); -} - -void LLScrollContainer::goToBottom() -{ - mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize()); - updateScroll(); -} - -S32 LLScrollContainer::getBorderWidth() const -{ - if (mBorder->getVisible()) - { - return mBorder->getBorderWidth(); - } - - return 0; -} - -void LLScrollContainer::setSize(S32 size) -{ - mSize = size; - mScrollbar[VERTICAL]->setThickness(size); - mScrollbar[HORIZONTAL]->setThickness(size); -} +/** + * @file llscrollcontainer.cpp + * @brief LLScrollContainer base class + * + * $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 "linden_common.h" + +#include "llscrollcontainer.h" + +#include "llrender.h" +#include "llcontainerview.h" +#include "lllocalcliprect.h" +// #include "llfolderview.h" +#include "llscrollingpanellist.h" +#include "llscrollbar.h" +#include "llui.h" +#include "llkeyboard.h" +#include "llviewborder.h" +#include "llfocusmgr.h" +#include "llframetimer.h" +#include "lluictrlfactory.h" +#include "llpanel.h" +#include "llfontgl.h" + +///---------------------------------------------------------------------------- +/// Local function declarations, constants, enums, and typedefs +///---------------------------------------------------------------------------- + +static const S32 VERTICAL_MULTIPLE = 16; +static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; + +///---------------------------------------------------------------------------- +/// Class LLScrollContainer +///---------------------------------------------------------------------------- + +static LLDefaultChildRegistry::Register r("scroll_container"); + +#include "llscrollingpanellist.h" +#include "llcontainerview.h" +#include "llpanel.h" + +static ScrollContainerRegistry::Register r1("scrolling_panel_list"); +static ScrollContainerRegistry::Register r2("container_view"); +static ScrollContainerRegistry::Register r3("panel", &LLPanel::fromXML); + +LLScrollContainer::Params::Params() +: is_opaque("opaque"), + bg_color("color"), + border_visible("border_visible"), + hide_scrollbar("hide_scrollbar"), + ignore_arrow_keys("ignore_arrow_keys"), + min_auto_scroll_rate("min_auto_scroll_rate", 100), + max_auto_scroll_rate("max_auto_scroll_rate", 1000), + max_auto_scroll_zone("max_auto_scroll_zone", 16), + reserve_scroll_corner("reserve_scroll_corner", false), + size("size", -1) +{} + + +// Default constructor +LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) +: LLUICtrl(p), + mAutoScrolling( false ), + mAutoScrollRate( 0.f ), + mBackgroundColor(p.bg_color()), + mIsOpaque(p.is_opaque), + mHideScrollbar(p.hide_scrollbar), + mIgnoreArrowKeys(p.ignore_arrow_keys), + mReserveScrollCorner(p.reserve_scroll_corner), + mMinAutoScrollRate(p.min_auto_scroll_rate), + mMaxAutoScrollRate(p.max_auto_scroll_rate), + mMaxAutoScrollZone(p.max_auto_scroll_zone), + mScrolledView(NULL), + mSize(p.size) +{ + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + + LLRect border_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 ); + LLViewBorder::Params params; + params.name("scroll border"); + params.rect(border_rect); + params.visible(p.border_visible); + params.bevel_style(LLViewBorder::BEVEL_IN); + mBorder = LLUICtrlFactory::create (params); + LLView::addChild( mBorder ); + + mInnerRect = getLocalRect(); + mInnerRect.stretch( -getBorderWidth() ); + + LLRect vertical_scroll_rect = mInnerRect; + vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size; + LLScrollbar::Params sbparams; + sbparams.name("scrollable vertical"); + sbparams.rect(vertical_scroll_rect); + sbparams.orientation(LLScrollbar::VERTICAL); + sbparams.doc_size(mInnerRect.getHeight()); + sbparams.doc_pos(0); + sbparams.page_size(mInnerRect.getHeight()); + sbparams.step_size(VERTICAL_MULTIPLE); + sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + sbparams.visible(false); + sbparams.change_callback(p.scroll_callback); + mScrollbar[VERTICAL] = LLUICtrlFactory::create (sbparams); + LLView::addChild( mScrollbar[VERTICAL] ); + + LLRect horizontal_scroll_rect; + horizontal_scroll_rect.mTop = scrollbar_size; + horizontal_scroll_rect.mRight = mInnerRect.getWidth(); + sbparams.name("scrollable horizontal"); + sbparams.rect(horizontal_scroll_rect); + sbparams.orientation(LLScrollbar::HORIZONTAL); + sbparams.doc_size(mInnerRect.getWidth()); + sbparams.doc_pos(0); + sbparams.page_size(mInnerRect.getWidth()); + sbparams.step_size(VERTICAL_MULTIPLE); + sbparams.visible(false); + sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); + sbparams.change_callback(p.scroll_callback); + mScrollbar[HORIZONTAL] = LLUICtrlFactory::create (sbparams); + LLView::addChild( mScrollbar[HORIZONTAL] ); +} + +// Destroys the object +LLScrollContainer::~LLScrollContainer( void ) +{ + // mScrolledView and mScrollbar are child views, so the LLView + // destructor takes care of memory deallocation. + for( S32 i = 0; i < ORIENTATION_COUNT; i++ ) + { + mScrollbar[i] = NULL; + } + mScrolledView = NULL; +} + +// internal scrollbar handlers +// virtual +void LLScrollContainer::scrollHorizontal( S32 new_pos ) +{ + if( mScrolledView ) + { + LLRect doc_rect = mScrolledView->getRect(); + S32 old_pos = -(doc_rect.mLeft - mInnerRect.mLeft); + mScrolledView->translate( -(new_pos - old_pos), 0 ); + } +} + +// virtual +void LLScrollContainer::scrollVertical( S32 new_pos ) +{ + if( mScrolledView ) + { + LLRect doc_rect = mScrolledView->getRect(); + S32 old_pos = doc_rect.mTop - mInnerRect.mTop; + mScrolledView->translate( 0, new_pos - old_pos ); + } +} + +// LLView functionality +void LLScrollContainer::reshape(S32 width, S32 height, + bool called_from_parent) +{ + LLUICtrl::reshape( width, height, called_from_parent ); + + mInnerRect = getLocalRect(); + mInnerRect.stretch( -getBorderWidth() ); + + if (mScrolledView) + { + const LLRect& scrolled_rect = mScrolledView->getRect(); + + 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 ); + + mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); + mScrollbar[VERTICAL]->setPageSize( visible_height ); + + mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); + mScrollbar[HORIZONTAL]->setPageSize( visible_width ); + updateScroll(); + } +} + +// virtual +bool LLScrollContainer::handleKeyHere(KEY key, MASK mask) +{ + if (mIgnoreArrowKeys) + { + switch(key) + { + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + case KEY_PAGE_UP: + case KEY_PAGE_DOWN: + case KEY_HOME: + case KEY_END: + return false; + default: + break; + } + } + + // 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 < ORIENTATION_COUNT; i++ ) + { + if( mScrollbar[i]->handleKeyHere(key, mask) ) + { + updateScroll(); + return true; + } + } + + return false; +} + +bool LLScrollContainer::handleUnicodeCharHere(llwchar uni_char) +{ + if (mScrolledView && mScrolledView->handleUnicodeCharHere(uni_char)) + { + return true; + } + return false; +} + +bool LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) +{ + // 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 (vertical->handleScrollWheel( 0, 0, clicks ) ) + { + updateScroll(); + } + // Always eat the event + return true; + } + + LLScrollbar* horizontal = mScrollbar[HORIZONTAL]; + // Test enablement and visibility for consistency with + // LLView::childrenHandleScrollWheel(). + if (horizontal->getVisible() + && horizontal->getEnabled() + && horizontal->handleScrollWheel( 0, 0, clicks ) ) + { + updateScroll(); + return true; + } + return false; +} + +bool LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks) +{ + if (LLUICtrl::handleScrollHWheel(x,y,clicks)) + { + return true; + } + + LLScrollbar* horizontal = mScrollbar[HORIZONTAL]; + if (horizontal->getVisible() + && horizontal->getEnabled() + && horizontal->handleScrollHWheel( 0, 0, clicks ) ) + { + updateScroll(); + return true; + } + + return false; +} + +bool LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + // Scroll folder view if needed. Never accepts a drag or drop. + *accept = ACCEPT_NO; + bool handled = autoScroll(x, y); + + if( !handled ) + { + handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, + cargo_data, accept, tooltip_msg) != NULL; + } + + return true; +} + +bool LLScrollContainer::canAutoScroll(S32 x, S32 y) +{ + if (mAutoScrolling) + { + return true; // already scrolling + } + return autoScroll(x, y, false); +} + +bool LLScrollContainer::autoScroll(S32 x, S32 y) +{ + return autoScroll(x, y, true); +} + +bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll) +{ + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + + 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 ); + // Note: Will also include scrollers as scroll zones, so opposite + // scroll zones might have different size due to visible scrollers + 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 = ll_round(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, (S32)mMaxAutoScrollZone); + S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, (S32)mMaxAutoScrollZone); + + 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) ) + { + if (do_scroll) + { + 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()) ) + { + if (do_scroll) + { + 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()) ) + { + if (do_scroll) + { + 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) ) + { + if (do_scroll) + { + mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed); + mAutoScrolling = true; + } + scrolling = true; + } + } + } + return scrolling; +} + +void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const +{ + const LLRect& doc_rect = getScrolledViewRect(); + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + + S32 doc_width = doc_rect.getWidth(); + S32 doc_height = doc_rect.getHeight(); + + S32 border_width = getBorderWidth(); + *visible_width = getRect().getWidth() - 2 * border_width; + *visible_height = getRect().getHeight() - 2 * border_width; + + *show_v_scrollbar = false; + *show_h_scrollbar = false; + + if (!mHideScrollbar) + { + // Note: 1 pixel change can happen on final animation and should not trigger + // the display of sliders. + if ((doc_height - *visible_height) > 1) + { + *show_v_scrollbar = true; + *visible_width -= scrollbar_size; + } + if ((doc_width - *visible_width) > 1) + { + *show_h_scrollbar = true; + *visible_height -= scrollbar_size; + // Note: Do *not* recompute *show_v_scrollbar here because with + // The view inside the scroll container should not be extended + // to container's full height to ensure the correct computation + // of *show_v_scrollbar after subtracting horizontal scrollbar_size. + + if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) ) + { + *show_v_scrollbar = true; + *visible_width -= scrollbar_size; + } + } + } +} + + +void LLScrollContainer::draw() +{ + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + + if (mAutoScrolling) + { + // add acceleration to autoscroll + mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), mMaxAutoScrollRate); + } + else + { + // reset to minimum for next time + mAutoScrollRate = mMinAutoScrollRate; + } + // 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 (!hasFocus() + && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture())) + { + focusFirstItem(); + } + + if (getRect().isValid()) + { + // Draw background + if( mIsOpaque ) + { + F32 alpha = getCurrentTransparency(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha); + } + + // 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. + { + 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) ); + } + + // 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 ) + { + sDepth++; + } + if( (viewp != mScrolledView) && viewp->getVisible() ) + { + drawChild(viewp); + } + if( sDebugRects ) + { + sDepth--; + } + } + } +} // end draw + +bool LLScrollContainer::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 LLScrollContainer::updateScroll() +{ + if (!getVisible() || !mScrolledView) + { + return; + } + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + + LLRect doc_rect = mScrolledView->getRect(); + S32 doc_width = doc_rect.getWidth(); + S32 doc_height = doc_rect.getHeight(); + 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 ); + + S32 border_width = getBorderWidth(); + if( show_v_scrollbar ) + { + if( doc_rect.mTop < getRect().getHeight() - border_width ) + { + mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop ); + } + + scrollVertical( mScrollbar[VERTICAL]->getDocPos() ); + mScrollbar[VERTICAL]->setVisible( true ); + + S32 v_scrollbar_height = visible_height; + if( !show_h_scrollbar && mReserveScrollCorner ) + { + v_scrollbar_height -= scrollbar_size; + } + 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; + } + LLRect r = mScrollbar[VERTICAL]->getRect(); + r.translate( 0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset ); + mScrollbar[VERTICAL]->setRect( r ); + } + else + { + mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop ); + + mScrollbar[VERTICAL]->setVisible( false ); + mScrollbar[VERTICAL]->setDocPos( 0 ); + } + + if( show_h_scrollbar ) + { + if( doc_rect.mLeft > border_width ) + { + mScrolledView->translate( border_width - doc_rect.mLeft, 0 ); + mScrollbar[HORIZONTAL]->setDocPos( 0 ); + } + else + { + scrollHorizontal( mScrollbar[HORIZONTAL]->getDocPos() ); + } + + mScrollbar[HORIZONTAL]->setVisible( true ); + S32 h_scrollbar_width = visible_width; + if( !show_v_scrollbar && mReserveScrollCorner ) + { + h_scrollbar_width -= scrollbar_size; + } + mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, scrollbar_size, true ); + } + else + { + mScrolledView->translate( border_width - doc_rect.mLeft, 0 ); + + mScrollbar[HORIZONTAL]->setVisible( false ); + mScrollbar[HORIZONTAL]->setDocPos( 0 ); + } + + mScrollbar[HORIZONTAL]->setDocSize( doc_width ); + mScrollbar[HORIZONTAL]->setPageSize( visible_width ); + + mScrollbar[VERTICAL]->setDocSize( doc_height ); + mScrollbar[VERTICAL]->setPageSize( visible_height ); +} // end updateScroll + +void LLScrollContainer::setBorderVisible(bool b) +{ + mBorder->setVisible( b ); + // Recompute inner rect, as border visibility changes it + mInnerRect = getLocalRect(); + mInnerRect.stretch( -getBorderWidth() ); +} + +LLRect LLScrollContainer::getVisibleContentRect() +{ + updateScroll(); + LLRect visible_rect = getContentWindowRect(); + LLRect contents_rect = mScrolledView->getRect(); + visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom); + return visible_rect; +} + +LLRect LLScrollContainer::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 LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& constraint) +{ + if (!mScrolledView) + { + LL_WARNS() << "LLScrollContainer::scrollToShowRect with no view!" << LL_ENDL; + return; + } + + LLRect content_window_rect = getContentWindowRect(); + // get document rect + LLRect scrolled_rect = mScrolledView->getRect(); + + // 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()); + + // 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); + + // 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( content_window_rect.getHeight() ); + mScrollbar[VERTICAL]->setDocPos( vert_pos ); + + S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(), + allowable_scroll_rect.mLeft, + allowable_scroll_rect.mRight); + + mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); + 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 LLScrollContainer::pageUp(S32 overlap) +{ + mScrollbar[VERTICAL]->pageUp(overlap); + updateScroll(); +} + +void LLScrollContainer::pageDown(S32 overlap) +{ + mScrollbar[VERTICAL]->pageDown(overlap); + updateScroll(); +} + +void LLScrollContainer::goToTop() +{ + mScrollbar[VERTICAL]->setDocPos(0); + updateScroll(); +} + +void LLScrollContainer::goToBottom() +{ + mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize()); + updateScroll(); +} + +S32 LLScrollContainer::getBorderWidth() const +{ + if (mBorder->getVisible()) + { + return mBorder->getBorderWidth(); + } + + return 0; +} + +void LLScrollContainer::setSize(S32 size) +{ + mSize = size; + mScrollbar[VERTICAL]->setThickness(size); + mScrollbar[HORIZONTAL]->setThickness(size); +} diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index 7e9532822b..859750d71c 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -1,157 +1,157 @@ -/** - * @file llscrollcontainer.h - * @brief LLScrollContainer class header file. - * - * $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$ - */ - -#ifndef LL_LLSCROLLCONTAINER_H -#define LL_LLSCROLLCONTAINER_H - -#include "lluictrl.h" -#ifndef LL_V4COLOR_H -#include "v4color.h" -#endif -#include "llcoord.h" -#include "llscrollbar.h" - - -class LLViewBorder; -class LLUICtrlFactory; - -/***************************************************************************** - * - * A decorator view class meant to encapsulate a clipped region which is - * scrollable. It automatically takes care of pixel perfect scrolling - * and cliipping, as well as turning the scrollbars on or off based on - * the width and height of the view you're scrolling. - * - *****************************************************************************/ - -struct ScrollContainerRegistry : public LLChildRegistry -{ - LLSINGLETON_EMPTY_CTOR(ScrollContainerRegistry); -}; - -class LLScrollContainer : public LLUICtrl -{ -public: - // Note: vertical comes before horizontal because vertical - // scrollbars have priority for mouse and keyboard events. - - struct Params : public LLInitParam::Block - { - Optional is_opaque, - reserve_scroll_corner, - border_visible, - hide_scrollbar, - ignore_arrow_keys; - Optional min_auto_scroll_rate, - max_auto_scroll_rate; - Optional max_auto_scroll_zone; - Optional bg_color; - Optional scroll_callback; - Optional size; - - Params(); - }; - - // my valid children are stored in this registry - typedef ScrollContainerRegistry child_registry_t; - -protected: - LLScrollContainer(const Params&); - friend class LLUICtrlFactory; -public: - virtual ~LLScrollContainer( void ); - - virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); } - - void setBorderVisible( bool b ); - - 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; } - LLRect getVisibleContentRect(); - LLRect getContentWindowRect(); - virtual 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() const { return mScrollbar[VERTICAL]->isAtBeginning(); } - bool isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); } - S32 getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); } - S32 getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); } - S32 getBorderWidth() const; - - // LLView functionality - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - virtual bool handleKeyHere(KEY key, MASK mask); - virtual bool handleUnicodeCharHere(llwchar uni_char); - virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); - virtual bool handleScrollHWheel( S32 x, S32 y, S32 clicks ); - 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 bool addChild(LLView* view, S32 tab_group = 0); - - bool canAutoScroll(S32 x, S32 y); - bool autoScroll(S32 x, S32 y); - - S32 getSize() const { return mSize; } - void setSize(S32 thickness); - -protected: - LLView* mScrolledView; - -private: - // internal scrollbar handlers - virtual void scrollHorizontal( S32 new_pos ); - virtual void scrollVertical( S32 new_pos ); - void updateScroll(); - bool autoScroll(S32 x, S32 y, bool do_scroll); - void calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const; - - LLScrollbar* mScrollbar[ORIENTATION_COUNT]; - S32 mSize; - bool mIsOpaque; - LLUIColor mBackgroundColor; - LLRect mInnerRect; - LLViewBorder* mBorder; - bool mReserveScrollCorner; - bool mAutoScrolling; - F32 mAutoScrollRate; - F32 mMinAutoScrollRate; - F32 mMaxAutoScrollRate; - U32 mMaxAutoScrollZone; - bool mHideScrollbar; - bool mIgnoreArrowKeys; -}; - - -#endif // LL_LLSCROLLCONTAINER_H +/** + * @file llscrollcontainer.h + * @brief LLScrollContainer class header file. + * + * $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$ + */ + +#ifndef LL_LLSCROLLCONTAINER_H +#define LL_LLSCROLLCONTAINER_H + +#include "lluictrl.h" +#ifndef LL_V4COLOR_H +#include "v4color.h" +#endif +#include "llcoord.h" +#include "llscrollbar.h" + + +class LLViewBorder; +class LLUICtrlFactory; + +/***************************************************************************** + * + * A decorator view class meant to encapsulate a clipped region which is + * scrollable. It automatically takes care of pixel perfect scrolling + * and cliipping, as well as turning the scrollbars on or off based on + * the width and height of the view you're scrolling. + * + *****************************************************************************/ + +struct ScrollContainerRegistry : public LLChildRegistry +{ + LLSINGLETON_EMPTY_CTOR(ScrollContainerRegistry); +}; + +class LLScrollContainer : public LLUICtrl +{ +public: + // Note: vertical comes before horizontal because vertical + // scrollbars have priority for mouse and keyboard events. + + struct Params : public LLInitParam::Block + { + Optional is_opaque, + reserve_scroll_corner, + border_visible, + hide_scrollbar, + ignore_arrow_keys; + Optional min_auto_scroll_rate, + max_auto_scroll_rate; + Optional max_auto_scroll_zone; + Optional bg_color; + Optional scroll_callback; + Optional size; + + Params(); + }; + + // my valid children are stored in this registry + typedef ScrollContainerRegistry child_registry_t; + +protected: + LLScrollContainer(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLScrollContainer( void ); + + virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); } + + void setBorderVisible( bool b ); + + 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; } + LLRect getVisibleContentRect(); + LLRect getContentWindowRect(); + virtual 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() const { return mScrollbar[VERTICAL]->isAtBeginning(); } + bool isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); } + S32 getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); } + S32 getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); } + S32 getBorderWidth() const; + + // LLView functionality + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + virtual bool handleKeyHere(KEY key, MASK mask); + virtual bool handleUnicodeCharHere(llwchar uni_char); + virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks ); + virtual bool handleScrollHWheel( S32 x, S32 y, S32 clicks ); + 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 bool addChild(LLView* view, S32 tab_group = 0); + + bool canAutoScroll(S32 x, S32 y); + bool autoScroll(S32 x, S32 y); + + S32 getSize() const { return mSize; } + void setSize(S32 thickness); + +protected: + LLView* mScrolledView; + +private: + // internal scrollbar handlers + virtual void scrollHorizontal( S32 new_pos ); + virtual void scrollVertical( S32 new_pos ); + void updateScroll(); + bool autoScroll(S32 x, S32 y, bool do_scroll); + void calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const; + + LLScrollbar* mScrollbar[ORIENTATION_COUNT]; + S32 mSize; + bool mIsOpaque; + LLUIColor mBackgroundColor; + LLRect mInnerRect; + LLViewBorder* mBorder; + bool mReserveScrollCorner; + bool mAutoScrolling; + F32 mAutoScrollRate; + F32 mMinAutoScrollRate; + F32 mMaxAutoScrollRate; + U32 mMaxAutoScrollZone; + bool mHideScrollbar; + bool mIgnoreArrowKeys; +}; + + +#endif // LL_LLSCROLLCONTAINER_H diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index 5c6b528afc..b158d7b1b7 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -1,252 +1,252 @@ -/** - * @file llscrollingpanellist.cpp - * @brief - * - * $LicenseInfo:firstyear=2006&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 "llstl.h" - -#include "llscrollingpanellist.h" - -static LLDefaultChildRegistry::Register r("scrolling_panel_list"); - - -///////////////////////////////////////////////////////////////////// -// LLScrollingPanelList - -// This could probably be integrated with LLScrollContainer -SJB - -LLScrollingPanelList::Params::Params() - : is_horizontal("is_horizontal") - , padding("padding") - , spacing("spacing") -{ -} - -LLScrollingPanelList::LLScrollingPanelList(const Params& p) - : LLUICtrl(p) - , mIsHorizontal(p.is_horizontal) - , mPadding(p.padding.isProvided() ? p.padding : DEFAULT_PADDING) - , mSpacing(p.spacing.isProvided() ? p.spacing : DEFAULT_SPACING) -{ -} - -void LLScrollingPanelList::clearPanels() -{ - deleteAllChildren(); - mPanelList.clear(); - rearrange(); -} - -S32 LLScrollingPanelList::addPanel(LLScrollingPanel* panel, bool back) -{ - if (back) - { - addChild(panel); - mPanelList.push_back(panel); - } - else - { - addChildInBack(panel); - mPanelList.push_front(panel); - } - - rearrange(); - - return mIsHorizontal ? getRect().getWidth() : getRect().getHeight(); -} - -void LLScrollingPanelList::removePanel(LLScrollingPanel* panel) -{ - U32 index = 0; - LLScrollingPanelList::panel_list_t::const_iterator iter; - - if (!mPanelList.empty()) - { - for (iter = mPanelList.begin(); iter != mPanelList.end(); ++iter, ++index) - { - if (*iter == panel) - { - break; - } - } - if (iter != mPanelList.end()) - { - removePanel(index); - } - } -} - -void LLScrollingPanelList::removePanel( U32 panel_index ) -{ - if ( mPanelList.empty() || panel_index >= mPanelList.size() ) - { - LL_WARNS() << "Panel index " << panel_index << " is out of range!" << LL_ENDL; - return; - } - else - { - removeChild( mPanelList.at(panel_index) ); - mPanelList.erase( mPanelList.begin() + panel_index ); - } - - rearrange(); -} - -void LLScrollingPanelList::updatePanels(bool allow_modify) -{ - for (std::deque::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) - { - LLScrollingPanel *childp = *iter; - childp->updatePanel(allow_modify); - } -} - -void LLScrollingPanelList::rearrange() -{ - // Resize this view - S32 new_width, new_height; - if (!mPanelList.empty()) - { - new_width = new_height = mPadding * 2; - for (std::deque::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) - { - LLScrollingPanel* childp = *iter; - const LLRect& rect = childp->getRect(); - if (mIsHorizontal) - { - new_width += rect.getWidth() + mSpacing; - new_height = llmax(new_height, rect.getHeight()); - } - else - { - new_height += rect.getHeight() + mSpacing; - new_width = llmax(new_width, rect.getWidth()); - } - } - - if (mIsHorizontal) - { - new_width -= mSpacing; - } - else - { - new_height -= mSpacing; - } - } - else - { - new_width = new_height = 1; - } - - LLRect rc = getRect(); - if (mIsHorizontal || !followsRight()) - { - rc.mRight = rc.mLeft + new_width; - } - if (!mIsHorizontal || !followsBottom()) - { - rc.mBottom = rc.mTop - new_height; - } - - if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom) - { - setRect(rc); - notifySizeChanged(); - } - - // Reposition each of the child views - S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding; - for (std::deque::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) - { - LLScrollingPanel* childp = *iter; - const LLRect& rect = childp->getRect(); - if (mIsHorizontal) - { - childp->translate(pos - rect.mLeft, rc.getHeight() - mPadding - rect.mTop); - pos += rect.getWidth() + mSpacing; - } - else - { - childp->translate(mPadding - rect.mLeft, pos - rect.mTop); - pos -= rect.getHeight() + mSpacing; - } - } -} - -void LLScrollingPanelList::updatePanelVisiblilty() -{ - // Determine visibility of children. - - LLRect parent_screen_rect; - getParent()->localPointToScreen( - mPadding, mPadding, - &parent_screen_rect.mLeft, &parent_screen_rect.mBottom ); - getParent()->localPointToScreen( - getParent()->getRect().getWidth() - mPadding, - getParent()->getRect().getHeight() - mPadding, - &parent_screen_rect.mRight, &parent_screen_rect.mTop ); - - for (std::deque::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) - { - LLScrollingPanel *childp = *iter; - const LLRect& local_rect = childp->getRect(); - LLRect screen_rect; - childp->localPointToScreen( - 0, 0, - &screen_rect.mLeft, &screen_rect.mBottom ); - childp->localPointToScreen( - local_rect.getWidth(), local_rect.getHeight(), - &screen_rect.mRight, &screen_rect.mTop ); - - bool intersects = - ( (screen_rect.mRight > parent_screen_rect.mLeft) && (screen_rect.mLeft < parent_screen_rect.mRight) ) && - ( (screen_rect.mTop > parent_screen_rect.mBottom) && (screen_rect.mBottom < parent_screen_rect.mTop) ); - - childp->setVisible( intersects ); - } -} - - -void LLScrollingPanelList::draw() -{ - updatePanelVisiblilty(); - - LLUICtrl::draw(); -} - -void LLScrollingPanelList::notifySizeChanged() -{ - LLSD info; - info["action"] = "size_changes"; - info["height"] = getRect().getHeight(); - info["width"] = getRect().getWidth(); - notifyParent(info); -} - -// EOF +/** + * @file llscrollingpanellist.cpp + * @brief + * + * $LicenseInfo:firstyear=2006&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 "llstl.h" + +#include "llscrollingpanellist.h" + +static LLDefaultChildRegistry::Register r("scrolling_panel_list"); + + +///////////////////////////////////////////////////////////////////// +// LLScrollingPanelList + +// This could probably be integrated with LLScrollContainer -SJB + +LLScrollingPanelList::Params::Params() + : is_horizontal("is_horizontal") + , padding("padding") + , spacing("spacing") +{ +} + +LLScrollingPanelList::LLScrollingPanelList(const Params& p) + : LLUICtrl(p) + , mIsHorizontal(p.is_horizontal) + , mPadding(p.padding.isProvided() ? p.padding : DEFAULT_PADDING) + , mSpacing(p.spacing.isProvided() ? p.spacing : DEFAULT_SPACING) +{ +} + +void LLScrollingPanelList::clearPanels() +{ + deleteAllChildren(); + mPanelList.clear(); + rearrange(); +} + +S32 LLScrollingPanelList::addPanel(LLScrollingPanel* panel, bool back) +{ + if (back) + { + addChild(panel); + mPanelList.push_back(panel); + } + else + { + addChildInBack(panel); + mPanelList.push_front(panel); + } + + rearrange(); + + return mIsHorizontal ? getRect().getWidth() : getRect().getHeight(); +} + +void LLScrollingPanelList::removePanel(LLScrollingPanel* panel) +{ + U32 index = 0; + LLScrollingPanelList::panel_list_t::const_iterator iter; + + if (!mPanelList.empty()) + { + for (iter = mPanelList.begin(); iter != mPanelList.end(); ++iter, ++index) + { + if (*iter == panel) + { + break; + } + } + if (iter != mPanelList.end()) + { + removePanel(index); + } + } +} + +void LLScrollingPanelList::removePanel( U32 panel_index ) +{ + if ( mPanelList.empty() || panel_index >= mPanelList.size() ) + { + LL_WARNS() << "Panel index " << panel_index << " is out of range!" << LL_ENDL; + return; + } + else + { + removeChild( mPanelList.at(panel_index) ); + mPanelList.erase( mPanelList.begin() + panel_index ); + } + + rearrange(); +} + +void LLScrollingPanelList::updatePanels(bool allow_modify) +{ + for (std::deque::iterator iter = mPanelList.begin(); + iter != mPanelList.end(); ++iter) + { + LLScrollingPanel *childp = *iter; + childp->updatePanel(allow_modify); + } +} + +void LLScrollingPanelList::rearrange() +{ + // Resize this view + S32 new_width, new_height; + if (!mPanelList.empty()) + { + new_width = new_height = mPadding * 2; + for (std::deque::iterator iter = mPanelList.begin(); + iter != mPanelList.end(); ++iter) + { + LLScrollingPanel* childp = *iter; + const LLRect& rect = childp->getRect(); + if (mIsHorizontal) + { + new_width += rect.getWidth() + mSpacing; + new_height = llmax(new_height, rect.getHeight()); + } + else + { + new_height += rect.getHeight() + mSpacing; + new_width = llmax(new_width, rect.getWidth()); + } + } + + if (mIsHorizontal) + { + new_width -= mSpacing; + } + else + { + new_height -= mSpacing; + } + } + else + { + new_width = new_height = 1; + } + + LLRect rc = getRect(); + if (mIsHorizontal || !followsRight()) + { + rc.mRight = rc.mLeft + new_width; + } + if (!mIsHorizontal || !followsBottom()) + { + rc.mBottom = rc.mTop - new_height; + } + + if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom) + { + setRect(rc); + notifySizeChanged(); + } + + // Reposition each of the child views + S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding; + for (std::deque::iterator iter = mPanelList.begin(); + iter != mPanelList.end(); ++iter) + { + LLScrollingPanel* childp = *iter; + const LLRect& rect = childp->getRect(); + if (mIsHorizontal) + { + childp->translate(pos - rect.mLeft, rc.getHeight() - mPadding - rect.mTop); + pos += rect.getWidth() + mSpacing; + } + else + { + childp->translate(mPadding - rect.mLeft, pos - rect.mTop); + pos -= rect.getHeight() + mSpacing; + } + } +} + +void LLScrollingPanelList::updatePanelVisiblilty() +{ + // Determine visibility of children. + + LLRect parent_screen_rect; + getParent()->localPointToScreen( + mPadding, mPadding, + &parent_screen_rect.mLeft, &parent_screen_rect.mBottom ); + getParent()->localPointToScreen( + getParent()->getRect().getWidth() - mPadding, + getParent()->getRect().getHeight() - mPadding, + &parent_screen_rect.mRight, &parent_screen_rect.mTop ); + + for (std::deque::iterator iter = mPanelList.begin(); + iter != mPanelList.end(); ++iter) + { + LLScrollingPanel *childp = *iter; + const LLRect& local_rect = childp->getRect(); + LLRect screen_rect; + childp->localPointToScreen( + 0, 0, + &screen_rect.mLeft, &screen_rect.mBottom ); + childp->localPointToScreen( + local_rect.getWidth(), local_rect.getHeight(), + &screen_rect.mRight, &screen_rect.mTop ); + + bool intersects = + ( (screen_rect.mRight > parent_screen_rect.mLeft) && (screen_rect.mLeft < parent_screen_rect.mRight) ) && + ( (screen_rect.mTop > parent_screen_rect.mBottom) && (screen_rect.mBottom < parent_screen_rect.mTop) ); + + childp->setVisible( intersects ); + } +} + + +void LLScrollingPanelList::draw() +{ + updatePanelVisiblilty(); + + LLUICtrl::draw(); +} + +void LLScrollingPanelList::notifySizeChanged() +{ + LLSD info; + info["action"] = "size_changes"; + info["height"] = getRect().getHeight(); + info["width"] = getRect().getWidth(); + notifyParent(info); +} + +// EOF diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h index 8e92e3d6aa..309ce4592d 100644 --- a/indra/llui/llscrollingpanellist.h +++ b/indra/llui/llscrollingpanellist.h @@ -1,102 +1,102 @@ -/** - * @file llscrollingpanellist.h - * - * $LicenseInfo:firstyear=2006&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_LLSCROLLINGPANELLIST_H -#define LL_LLSCROLLINGPANELLIST_H - -#include - -#include "llui.h" -#include "lluictrlfactory.h" -#include "llview.h" -#include "llpanel.h" - -/* - * Pure virtual class represents a scrolling panel. - */ -class LLScrollingPanel : public LLPanel -{ -public: - LLScrollingPanel(const LLPanel::Params& params) : LLPanel(params) {} - virtual void updatePanel(bool allow_modify) = 0; -}; - - -/* - * A set of panels that are displayed in a sequence inside a scroll container. - */ -class LLScrollingPanelList : public LLUICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Optional is_horizontal; - Optional padding; - Optional spacing; - - Params(); - }; - - LLScrollingPanelList(const Params& p); - - static const S32 DEFAULT_SPACING = 6; - static const S32 DEFAULT_PADDING = 2; - - typedef std::deque panel_list_t; - - virtual void setValue(const LLSD& value) {}; - - virtual void draw(); - - void clearPanels(); - S32 addPanel(LLScrollingPanel* panel, bool back = false); - void removePanel(LLScrollingPanel* panel); - void removePanel(U32 panel_index); - void updatePanels(bool allow_modify); - void rearrange(); - - const panel_list_t& getPanelList() const { return mPanelList; } - bool getIsHorizontal() const { return mIsHorizontal; } - void setPadding(S32 padding) { mPadding = padding; rearrange(); } - void setSpacing(S32 spacing) { mSpacing = spacing; rearrange(); } - S32 getPadding() const { return mPadding; } - S32 getSpacing() const { return mSpacing; } - -private: - void updatePanelVisiblilty(); - - /** - * Notify parent about size change, makes sense when used inside accordion - */ - void notifySizeChanged(); - - bool mIsHorizontal; - S32 mPadding; - S32 mSpacing; - - panel_list_t mPanelList; -}; - -#endif //LL_LLSCROLLINGPANELLIST_H +/** + * @file llscrollingpanellist.h + * + * $LicenseInfo:firstyear=2006&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_LLSCROLLINGPANELLIST_H +#define LL_LLSCROLLINGPANELLIST_H + +#include + +#include "llui.h" +#include "lluictrlfactory.h" +#include "llview.h" +#include "llpanel.h" + +/* + * Pure virtual class represents a scrolling panel. + */ +class LLScrollingPanel : public LLPanel +{ +public: + LLScrollingPanel(const LLPanel::Params& params) : LLPanel(params) {} + virtual void updatePanel(bool allow_modify) = 0; +}; + + +/* + * A set of panels that are displayed in a sequence inside a scroll container. + */ +class LLScrollingPanelList : public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional is_horizontal; + Optional padding; + Optional spacing; + + Params(); + }; + + LLScrollingPanelList(const Params& p); + + static const S32 DEFAULT_SPACING = 6; + static const S32 DEFAULT_PADDING = 2; + + typedef std::deque panel_list_t; + + virtual void setValue(const LLSD& value) {}; + + virtual void draw(); + + void clearPanels(); + S32 addPanel(LLScrollingPanel* panel, bool back = false); + void removePanel(LLScrollingPanel* panel); + void removePanel(U32 panel_index); + void updatePanels(bool allow_modify); + void rearrange(); + + const panel_list_t& getPanelList() const { return mPanelList; } + bool getIsHorizontal() const { return mIsHorizontal; } + void setPadding(S32 padding) { mPadding = padding; rearrange(); } + void setSpacing(S32 spacing) { mSpacing = spacing; rearrange(); } + S32 getPadding() const { return mPadding; } + S32 getSpacing() const { return mSpacing; } + +private: + void updatePanelVisiblilty(); + + /** + * Notify parent about size change, makes sense when used inside accordion + */ + void notifySizeChanged(); + + bool mIsHorizontal; + S32 mPadding; + S32 mSpacing; + + panel_list_t mPanelList; +}; + +#endif //LL_LLSCROLLINGPANELLIST_H diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index d58696a5c9..403879646d 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -1,671 +1,671 @@ -/** - * @file llscrolllistcell.cpp - * @brief Scroll lists are composed of rows (items), each of which - * contains columns (cells). - * - * $LicenseInfo:firstyear=2007&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 "llscrolllistcell.h" - -#include "llcheckboxctrl.h" -#include "llui.h" // LLUIImage -#include "lluictrlfactory.h" - -//static -LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_p) -{ - LLScrollListCell* cell = NULL; - - if (cell_p.type() == "icon") - { - cell = new LLScrollListIcon(cell_p); - } - else if (cell_p.type() == "checkbox") - { - cell = new LLScrollListCheck(cell_p); - } - else if (cell_p.type() == "date") - { - cell = new LLScrollListDate(cell_p); - } - else if (cell_p.type() == "icontext") - { - cell = new LLScrollListIconText(cell_p); - } - else if (cell_p.type() == "bar") - { - cell = new LLScrollListBar(cell_p); - } - else // default is "text" - { - cell = new LLScrollListText(cell_p); - } - - if (cell_p.value.isProvided()) - { - cell->setValue(cell_p.value); - } - - return cell; -} - - -LLScrollListCell::LLScrollListCell(const LLScrollListCell::Params& p) -: mWidth(p.width), - mToolTip(p.tool_tip) -{} - -// virtual -const LLSD LLScrollListCell::getValue() const -{ - return LLStringUtil::null; -} - - -// virtual -const LLSD LLScrollListCell::getAltValue() const -{ - return LLStringUtil::null; -} - - -// -// LLScrollListIcon -// -LLScrollListIcon::LLScrollListIcon(const LLScrollListCell::Params& p) -: LLScrollListCell(p), - mIcon(LLUI::getUIImage(p.value().asString())), - mColor(p.color), - mAlignment(p.font_halign) -{} - -LLScrollListIcon::~LLScrollListIcon() -{ -} - -/*virtual*/ -S32 LLScrollListIcon::getHeight() const -{ return mIcon ? mIcon->getHeight() : 0; } - -/*virtual*/ -const LLSD LLScrollListIcon::getValue() const -{ return mIcon.isNull() ? LLStringUtil::null : mIcon->getName(); } - -void LLScrollListIcon::setValue(const LLSD& value) -{ - if (value.isUUID()) - { - // don't use default image specified by LLUUID::null, use no image in that case - LLUUID image_id = value.asUUID(); - mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL); - } - else - { - std::string value_string = value.asString(); - if (LLUUID::validate(value_string)) - { - setValue(LLUUID(value_string)); - } - else if (!value_string.empty()) - { - mIcon = LLUI::getUIImage(value.asString()); - } - else - { - mIcon = NULL; - } - } -} - - -void LLScrollListIcon::setColor(const LLColor4& color) -{ - mColor = color; -} - -S32 LLScrollListIcon::getWidth() const -{ - // if no specified fix width, use width of icon - if (LLScrollListCell::getWidth() == 0 && mIcon.notNull()) - { - return mIcon->getWidth(); - } - return LLScrollListCell::getWidth(); -} - - -void LLScrollListIcon::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - if (mIcon) - { - switch(mAlignment) - { - case LLFontGL::LEFT: - mIcon->draw(0, 0, mColor); - break; - case LLFontGL::RIGHT: - mIcon->draw(getWidth() - mIcon->getWidth(), 0, mColor); - break; - case LLFontGL::HCENTER: - mIcon->draw((getWidth() - mIcon->getWidth()) / 2, 0, mColor); - break; - default: - break; - } - } -} - -// -// LLScrollListBar -// -LLScrollListBar::LLScrollListBar(const LLScrollListCell::Params& p) - : LLScrollListCell(p), - mRatio(0), - mColor(p.color), - mBottom(1), - mLeftPad(1), - mRightPad(1) -{} - -LLScrollListBar::~LLScrollListBar() -{ -} - -/*virtual*/ -S32 LLScrollListBar::getHeight() const -{ - return LLScrollListCell::getHeight(); -} - -/*virtual*/ -const LLSD LLScrollListBar::getValue() const -{ - return LLStringUtil::null; -} - -void LLScrollListBar::setValue(const LLSD& value) -{ - if (value.has("ratio")) - { - mRatio = value["ratio"].asReal(); - } - if (value.has("bottom")) - { - mBottom = value["bottom"].asInteger(); - } - if (value.has("left_pad")) - { - mLeftPad = value["left_pad"].asInteger(); - } - if (value.has("right_pad")) - { - mRightPad = value["right_pad"].asInteger(); - } -} - -void LLScrollListBar::setColor(const LLColor4& color) -{ - mColor = color; -} - -S32 LLScrollListBar::getWidth() const -{ - return LLScrollListCell::getWidth(); -} - - -void LLScrollListBar::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - S32 bar_width = getWidth() - mLeftPad - mRightPad; - S32 left = bar_width - bar_width * mRatio; - left = llclamp(left, mLeftPad, getWidth() - mRightPad - 1); - - gl_rect_2d(left, mBottom, getWidth() - mRightPad, mBottom - 1, mColor); -} - -// -// LLScrollListText -// -U32 LLScrollListText::sCount = 0; - -LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) -: LLScrollListCell(p), - mText(p.label.isProvided() ? p.label() : p.value().asString()), - mAltText(p.alt_value().asString()), - mFont(p.font), - mColor(p.color), - mUseColor(p.color.isProvided()), - mFontAlignment(p.font_halign), - mVisible(p.visible), - mHighlightCount( 0 ), - mHighlightOffset( 0 ) -{ - sCount++; - - mTextWidth = getWidth(); - - // initialize rounded rect image - if (!mRoundedRectImage) - { - mRoundedRectImage = LLUI::getUIImage("Rounded_Square"); - } -} - -//virtual -void LLScrollListText::highlightText(S32 offset, S32 num_chars) -{ - mHighlightOffset = offset; - mHighlightCount = llmax(0, num_chars); -} - -//virtual -bool LLScrollListText::isText() const -{ - return true; -} - -// virtual -const std::string &LLScrollListText::getToolTip() const -{ - // If base class has a tooltip, return that - if (! LLScrollListCell::getToolTip().empty()) - return LLScrollListCell::getToolTip(); - - // ...otherwise, return the value itself as the tooltip - return mText.getString(); -} - -// virtual -bool LLScrollListText::needsToolTip() const -{ - // If base class has a tooltip, return that - if (LLScrollListCell::needsToolTip()) - return LLScrollListCell::needsToolTip(); - - // ...otherwise, show tooltips for truncated text - return mFont->getWidth(mText.getString()) > getWidth(); -} - -//virtual -bool LLScrollListText::getVisible() const -{ - return mVisible; -} - -//virtual -S32 LLScrollListText::getHeight() const -{ - return mFont->getLineHeight(); -} - - -LLScrollListText::~LLScrollListText() -{ - sCount--; -} - -S32 LLScrollListText::getContentWidth() const -{ - return mFont->getWidth(mText.getString()); -} - - -void LLScrollListText::setColor(const LLColor4& color) -{ - mColor = color; - mUseColor = true; -} - -void LLScrollListText::setText(const LLStringExplicit& text) -{ - mText = text; -} - -void LLScrollListText::setFontStyle(const U8 font_style) -{ - LLFontDescriptor new_desc(mFont->getFontDesc()); - new_desc.setStyle(font_style); - mFont = LLFontGL::getFont(new_desc); -} - -//virtual -void LLScrollListText::setValue(const LLSD& text) -{ - setText(text.asString()); -} - -//virtual -void LLScrollListText::setAltValue(const LLSD& text) -{ - mAltText = text.asString(); -} - -//virtual -const LLSD LLScrollListText::getValue() const -{ - return LLSD(mText.getString()); -} - -//virtual -const LLSD LLScrollListText::getAltValue() const -{ - return LLSD(mAltText.getString()); -} - - -void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - LLColor4 display_color; - if (mUseColor) - { - display_color = mColor; - } - else - { - display_color = color; - } - - if (mHighlightCount > 0) - { - // Highlight text - S32 left = 0; - switch(mFontAlignment) - { - case LLFontGL::LEFT: - left = mFont->getWidth(mText.getString(), 1, mHighlightOffset); - break; - case LLFontGL::RIGHT: - left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); - break; - case LLFontGL::HCENTER: - left = (getWidth() - mFont->getWidth(mText.getString())) / 2; - break; - } - LLRect highlight_rect(left - 2, - mFont->getLineHeight() + 1, - left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, - 1); - mRoundedRectImage->draw(highlight_rect, highlight_color); - } - - // Try to draw the entire string - F32 right_x; - U32 string_chars = mText.length(); - F32 start_x = 0.f; - switch(mFontAlignment) - { - case LLFontGL::LEFT: - start_x = 1.f; - break; - case LLFontGL::RIGHT: - start_x = (F32)getWidth(); - break; - case LLFontGL::HCENTER: - start_x = (F32)getWidth() * 0.5f; - break; - } - mFont->render(mText.getWString(), 0, - start_x, 0.f, - display_color, - mFontAlignment, - LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - string_chars, - getTextWidth(), - &right_x, - true); -} - -// -// LLScrollListCheck -// -LLScrollListCheck::LLScrollListCheck(const LLScrollListCell::Params& p) -: LLScrollListCell(p) -{ - LLCheckBoxCtrl::Params checkbox_p; - checkbox_p.name("checkbox"); - checkbox_p.rect = LLRect(0, p.width, p.width, 0); - checkbox_p.enabled(p.enabled); - checkbox_p.initial_value(p.value()); - - mCheckBox = LLUICtrlFactory::create(checkbox_p); - - LLRect rect(mCheckBox->getRect()); - if (p.width) - { - rect.mRight = rect.mLeft + p.width; - mCheckBox->setRect(rect); - setWidth(p.width); - } - else - { - setWidth(rect.getWidth()); //check_box->getWidth(); - } - - mCheckBox->setColor(p.color); -} - - -LLScrollListCheck::~LLScrollListCheck() -{ - delete mCheckBox; - mCheckBox = NULL; -} - -void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - mCheckBox->draw(); -} - -bool LLScrollListCheck::handleClick() -{ - if (mCheckBox->getEnabled()) - { - mCheckBox->toggle(); - } - // don't change selection when clicking on embedded checkbox - return true; -} - -/*virtual*/ -const LLSD LLScrollListCheck::getValue() const -{ - return mCheckBox->getValue(); -} - -/*virtual*/ -void LLScrollListCheck::setValue(const LLSD& value) -{ - mCheckBox->setValue(value); -} - -/*virtual*/ -void LLScrollListCheck::onCommit() -{ - mCheckBox->onCommit(); -} - -/*virtual*/ -void LLScrollListCheck::setEnabled(bool enable) -{ - mCheckBox->setEnabled(enable); -} - -// -// LLScrollListDate -// - -LLScrollListDate::LLScrollListDate( const LLScrollListCell::Params& p) -: LLScrollListText(p), - mDate(p.value().asDate()) -{} - -void LLScrollListDate::setValue(const LLSD& value) -{ - mDate = value.asDate(); - LLScrollListText::setValue(mDate.asRFC1123()); -} - -const LLSD LLScrollListDate::getValue() const -{ - return mDate; -} - -// -// LLScrollListIconText -// -LLScrollListIconText::LLScrollListIconText(const LLScrollListCell::Params& p) - : LLScrollListText(p), - mIcon(p.value().isUUID() ? LLUI::getUIImageByID(p.value().asUUID()) : LLUI::getUIImage(p.value().asString())), - mPad(4) -{ - mTextWidth = getWidth() - mPad /*padding*/ - mFont->getLineHeight(); -} - -LLScrollListIconText::~LLScrollListIconText() -{ -} - -const LLSD LLScrollListIconText::getValue() const -{ - if (mIcon.isNull()) - { - return LLStringUtil::null; - } - return mIcon->getName(); -} - -void LLScrollListIconText::setValue(const LLSD& value) -{ - if (value.isUUID()) - { - // don't use default image specified by LLUUID::null, use no image in that case - LLUUID image_id = value.asUUID(); - mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL); - } - else - { - std::string value_string = value.asString(); - if (LLUUID::validate(value_string)) - { - setValue(LLUUID(value_string)); - } - else if (!value_string.empty()) - { - mIcon = LLUI::getUIImage(value.asString()); - } - else - { - mIcon = NULL; - } - } -} - -void LLScrollListIconText::setWidth(S32 width) -{ - LLScrollListCell::setWidth(width); - // Assume that iamge height and width is identical to font height and width - mTextWidth = width - mPad /*padding*/ - mFont->getLineHeight(); -} - - -void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - LLColor4 display_color; - if (mUseColor) - { - display_color = mColor; - } - else - { - display_color = color; - } - - S32 icon_height = mFont->getLineHeight(); - S32 icon_space = mIcon ? (icon_height + mPad) : 0; - - if (mHighlightCount > 0) - { - S32 left = 0; - switch (mFontAlignment) - { - case LLFontGL::LEFT: - left = mFont->getWidth(mText.getString(), icon_space + 1, mHighlightOffset); - break; - case LLFontGL::RIGHT: - left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX) - icon_space; - break; - case LLFontGL::HCENTER: - left = (getWidth() - mFont->getWidth(mText.getString()) - icon_space) / 2; - break; - } - LLRect highlight_rect(left - 2, - mFont->getLineHeight() + 1, - left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, - 1); - mRoundedRectImage->draw(highlight_rect, highlight_color); - } - - // Try to draw the entire string - F32 right_x; - U32 string_chars = mText.length(); - F32 start_text_x = 0.f; - S32 start_icon_x = 0; - switch (mFontAlignment) - { - case LLFontGL::LEFT: - start_text_x = icon_space + 1; - start_icon_x = 1; - break; - case LLFontGL::RIGHT: - start_text_x = (F32)getWidth(); - start_icon_x = getWidth() - mFont->getWidth(mText.getString()) - icon_space; - break; - case LLFontGL::HCENTER: - F32 center = (F32)getWidth()* 0.5f; - start_text_x = center + ((F32)icon_space * 0.5f); - start_icon_x = center - (((F32)icon_space + mFont->getWidth(mText.getString())) * 0.5f); - break; - } - mFont->render(mText.getWString(), 0, - start_text_x, 0.f, - display_color, - mFontAlignment, - LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - string_chars, - getTextWidth(), - &right_x, - true); - - if (mIcon) - { - mIcon->draw(start_icon_x, 0, icon_height, icon_height, mColor); - } -} - - +/** + * @file llscrolllistcell.cpp + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). + * + * $LicenseInfo:firstyear=2007&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 "llscrolllistcell.h" + +#include "llcheckboxctrl.h" +#include "llui.h" // LLUIImage +#include "lluictrlfactory.h" + +//static +LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_p) +{ + LLScrollListCell* cell = NULL; + + if (cell_p.type() == "icon") + { + cell = new LLScrollListIcon(cell_p); + } + else if (cell_p.type() == "checkbox") + { + cell = new LLScrollListCheck(cell_p); + } + else if (cell_p.type() == "date") + { + cell = new LLScrollListDate(cell_p); + } + else if (cell_p.type() == "icontext") + { + cell = new LLScrollListIconText(cell_p); + } + else if (cell_p.type() == "bar") + { + cell = new LLScrollListBar(cell_p); + } + else // default is "text" + { + cell = new LLScrollListText(cell_p); + } + + if (cell_p.value.isProvided()) + { + cell->setValue(cell_p.value); + } + + return cell; +} + + +LLScrollListCell::LLScrollListCell(const LLScrollListCell::Params& p) +: mWidth(p.width), + mToolTip(p.tool_tip) +{} + +// virtual +const LLSD LLScrollListCell::getValue() const +{ + return LLStringUtil::null; +} + + +// virtual +const LLSD LLScrollListCell::getAltValue() const +{ + return LLStringUtil::null; +} + + +// +// LLScrollListIcon +// +LLScrollListIcon::LLScrollListIcon(const LLScrollListCell::Params& p) +: LLScrollListCell(p), + mIcon(LLUI::getUIImage(p.value().asString())), + mColor(p.color), + mAlignment(p.font_halign) +{} + +LLScrollListIcon::~LLScrollListIcon() +{ +} + +/*virtual*/ +S32 LLScrollListIcon::getHeight() const +{ return mIcon ? mIcon->getHeight() : 0; } + +/*virtual*/ +const LLSD LLScrollListIcon::getValue() const +{ return mIcon.isNull() ? LLStringUtil::null : mIcon->getName(); } + +void LLScrollListIcon::setValue(const LLSD& value) +{ + if (value.isUUID()) + { + // don't use default image specified by LLUUID::null, use no image in that case + LLUUID image_id = value.asUUID(); + mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL); + } + else + { + std::string value_string = value.asString(); + if (LLUUID::validate(value_string)) + { + setValue(LLUUID(value_string)); + } + else if (!value_string.empty()) + { + mIcon = LLUI::getUIImage(value.asString()); + } + else + { + mIcon = NULL; + } + } +} + + +void LLScrollListIcon::setColor(const LLColor4& color) +{ + mColor = color; +} + +S32 LLScrollListIcon::getWidth() const +{ + // if no specified fix width, use width of icon + if (LLScrollListCell::getWidth() == 0 && mIcon.notNull()) + { + return mIcon->getWidth(); + } + return LLScrollListCell::getWidth(); +} + + +void LLScrollListIcon::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + if (mIcon) + { + switch(mAlignment) + { + case LLFontGL::LEFT: + mIcon->draw(0, 0, mColor); + break; + case LLFontGL::RIGHT: + mIcon->draw(getWidth() - mIcon->getWidth(), 0, mColor); + break; + case LLFontGL::HCENTER: + mIcon->draw((getWidth() - mIcon->getWidth()) / 2, 0, mColor); + break; + default: + break; + } + } +} + +// +// LLScrollListBar +// +LLScrollListBar::LLScrollListBar(const LLScrollListCell::Params& p) + : LLScrollListCell(p), + mRatio(0), + mColor(p.color), + mBottom(1), + mLeftPad(1), + mRightPad(1) +{} + +LLScrollListBar::~LLScrollListBar() +{ +} + +/*virtual*/ +S32 LLScrollListBar::getHeight() const +{ + return LLScrollListCell::getHeight(); +} + +/*virtual*/ +const LLSD LLScrollListBar::getValue() const +{ + return LLStringUtil::null; +} + +void LLScrollListBar::setValue(const LLSD& value) +{ + if (value.has("ratio")) + { + mRatio = value["ratio"].asReal(); + } + if (value.has("bottom")) + { + mBottom = value["bottom"].asInteger(); + } + if (value.has("left_pad")) + { + mLeftPad = value["left_pad"].asInteger(); + } + if (value.has("right_pad")) + { + mRightPad = value["right_pad"].asInteger(); + } +} + +void LLScrollListBar::setColor(const LLColor4& color) +{ + mColor = color; +} + +S32 LLScrollListBar::getWidth() const +{ + return LLScrollListCell::getWidth(); +} + + +void LLScrollListBar::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + S32 bar_width = getWidth() - mLeftPad - mRightPad; + S32 left = bar_width - bar_width * mRatio; + left = llclamp(left, mLeftPad, getWidth() - mRightPad - 1); + + gl_rect_2d(left, mBottom, getWidth() - mRightPad, mBottom - 1, mColor); +} + +// +// LLScrollListText +// +U32 LLScrollListText::sCount = 0; + +LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) +: LLScrollListCell(p), + mText(p.label.isProvided() ? p.label() : p.value().asString()), + mAltText(p.alt_value().asString()), + mFont(p.font), + mColor(p.color), + mUseColor(p.color.isProvided()), + mFontAlignment(p.font_halign), + mVisible(p.visible), + mHighlightCount( 0 ), + mHighlightOffset( 0 ) +{ + sCount++; + + mTextWidth = getWidth(); + + // initialize rounded rect image + if (!mRoundedRectImage) + { + mRoundedRectImage = LLUI::getUIImage("Rounded_Square"); + } +} + +//virtual +void LLScrollListText::highlightText(S32 offset, S32 num_chars) +{ + mHighlightOffset = offset; + mHighlightCount = llmax(0, num_chars); +} + +//virtual +bool LLScrollListText::isText() const +{ + return true; +} + +// virtual +const std::string &LLScrollListText::getToolTip() const +{ + // If base class has a tooltip, return that + if (! LLScrollListCell::getToolTip().empty()) + return LLScrollListCell::getToolTip(); + + // ...otherwise, return the value itself as the tooltip + return mText.getString(); +} + +// virtual +bool LLScrollListText::needsToolTip() const +{ + // If base class has a tooltip, return that + if (LLScrollListCell::needsToolTip()) + return LLScrollListCell::needsToolTip(); + + // ...otherwise, show tooltips for truncated text + return mFont->getWidth(mText.getString()) > getWidth(); +} + +//virtual +bool LLScrollListText::getVisible() const +{ + return mVisible; +} + +//virtual +S32 LLScrollListText::getHeight() const +{ + return mFont->getLineHeight(); +} + + +LLScrollListText::~LLScrollListText() +{ + sCount--; +} + +S32 LLScrollListText::getContentWidth() const +{ + return mFont->getWidth(mText.getString()); +} + + +void LLScrollListText::setColor(const LLColor4& color) +{ + mColor = color; + mUseColor = true; +} + +void LLScrollListText::setText(const LLStringExplicit& text) +{ + mText = text; +} + +void LLScrollListText::setFontStyle(const U8 font_style) +{ + LLFontDescriptor new_desc(mFont->getFontDesc()); + new_desc.setStyle(font_style); + mFont = LLFontGL::getFont(new_desc); +} + +//virtual +void LLScrollListText::setValue(const LLSD& text) +{ + setText(text.asString()); +} + +//virtual +void LLScrollListText::setAltValue(const LLSD& text) +{ + mAltText = text.asString(); +} + +//virtual +const LLSD LLScrollListText::getValue() const +{ + return LLSD(mText.getString()); +} + +//virtual +const LLSD LLScrollListText::getAltValue() const +{ + return LLSD(mAltText.getString()); +} + + +void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + LLColor4 display_color; + if (mUseColor) + { + display_color = mColor; + } + else + { + display_color = color; + } + + if (mHighlightCount > 0) + { + // Highlight text + S32 left = 0; + switch(mFontAlignment) + { + case LLFontGL::LEFT: + left = mFont->getWidth(mText.getString(), 1, mHighlightOffset); + break; + case LLFontGL::RIGHT: + left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); + break; + case LLFontGL::HCENTER: + left = (getWidth() - mFont->getWidth(mText.getString())) / 2; + break; + } + LLRect highlight_rect(left - 2, + mFont->getLineHeight() + 1, + left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, + 1); + mRoundedRectImage->draw(highlight_rect, highlight_color); + } + + // Try to draw the entire string + F32 right_x; + U32 string_chars = mText.length(); + F32 start_x = 0.f; + switch(mFontAlignment) + { + case LLFontGL::LEFT: + start_x = 1.f; + break; + case LLFontGL::RIGHT: + start_x = (F32)getWidth(); + break; + case LLFontGL::HCENTER: + start_x = (F32)getWidth() * 0.5f; + break; + } + mFont->render(mText.getWString(), 0, + start_x, 0.f, + display_color, + mFontAlignment, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + string_chars, + getTextWidth(), + &right_x, + true); +} + +// +// LLScrollListCheck +// +LLScrollListCheck::LLScrollListCheck(const LLScrollListCell::Params& p) +: LLScrollListCell(p) +{ + LLCheckBoxCtrl::Params checkbox_p; + checkbox_p.name("checkbox"); + checkbox_p.rect = LLRect(0, p.width, p.width, 0); + checkbox_p.enabled(p.enabled); + checkbox_p.initial_value(p.value()); + + mCheckBox = LLUICtrlFactory::create(checkbox_p); + + LLRect rect(mCheckBox->getRect()); + if (p.width) + { + rect.mRight = rect.mLeft + p.width; + mCheckBox->setRect(rect); + setWidth(p.width); + } + else + { + setWidth(rect.getWidth()); //check_box->getWidth(); + } + + mCheckBox->setColor(p.color); +} + + +LLScrollListCheck::~LLScrollListCheck() +{ + delete mCheckBox; + mCheckBox = NULL; +} + +void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + mCheckBox->draw(); +} + +bool LLScrollListCheck::handleClick() +{ + if (mCheckBox->getEnabled()) + { + mCheckBox->toggle(); + } + // don't change selection when clicking on embedded checkbox + return true; +} + +/*virtual*/ +const LLSD LLScrollListCheck::getValue() const +{ + return mCheckBox->getValue(); +} + +/*virtual*/ +void LLScrollListCheck::setValue(const LLSD& value) +{ + mCheckBox->setValue(value); +} + +/*virtual*/ +void LLScrollListCheck::onCommit() +{ + mCheckBox->onCommit(); +} + +/*virtual*/ +void LLScrollListCheck::setEnabled(bool enable) +{ + mCheckBox->setEnabled(enable); +} + +// +// LLScrollListDate +// + +LLScrollListDate::LLScrollListDate( const LLScrollListCell::Params& p) +: LLScrollListText(p), + mDate(p.value().asDate()) +{} + +void LLScrollListDate::setValue(const LLSD& value) +{ + mDate = value.asDate(); + LLScrollListText::setValue(mDate.asRFC1123()); +} + +const LLSD LLScrollListDate::getValue() const +{ + return mDate; +} + +// +// LLScrollListIconText +// +LLScrollListIconText::LLScrollListIconText(const LLScrollListCell::Params& p) + : LLScrollListText(p), + mIcon(p.value().isUUID() ? LLUI::getUIImageByID(p.value().asUUID()) : LLUI::getUIImage(p.value().asString())), + mPad(4) +{ + mTextWidth = getWidth() - mPad /*padding*/ - mFont->getLineHeight(); +} + +LLScrollListIconText::~LLScrollListIconText() +{ +} + +const LLSD LLScrollListIconText::getValue() const +{ + if (mIcon.isNull()) + { + return LLStringUtil::null; + } + return mIcon->getName(); +} + +void LLScrollListIconText::setValue(const LLSD& value) +{ + if (value.isUUID()) + { + // don't use default image specified by LLUUID::null, use no image in that case + LLUUID image_id = value.asUUID(); + mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL); + } + else + { + std::string value_string = value.asString(); + if (LLUUID::validate(value_string)) + { + setValue(LLUUID(value_string)); + } + else if (!value_string.empty()) + { + mIcon = LLUI::getUIImage(value.asString()); + } + else + { + mIcon = NULL; + } + } +} + +void LLScrollListIconText::setWidth(S32 width) +{ + LLScrollListCell::setWidth(width); + // Assume that iamge height and width is identical to font height and width + mTextWidth = width - mPad /*padding*/ - mFont->getLineHeight(); +} + + +void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + LLColor4 display_color; + if (mUseColor) + { + display_color = mColor; + } + else + { + display_color = color; + } + + S32 icon_height = mFont->getLineHeight(); + S32 icon_space = mIcon ? (icon_height + mPad) : 0; + + if (mHighlightCount > 0) + { + S32 left = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + left = mFont->getWidth(mText.getString(), icon_space + 1, mHighlightOffset); + break; + case LLFontGL::RIGHT: + left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX) - icon_space; + break; + case LLFontGL::HCENTER: + left = (getWidth() - mFont->getWidth(mText.getString()) - icon_space) / 2; + break; + } + LLRect highlight_rect(left - 2, + mFont->getLineHeight() + 1, + left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, + 1); + mRoundedRectImage->draw(highlight_rect, highlight_color); + } + + // Try to draw the entire string + F32 right_x; + U32 string_chars = mText.length(); + F32 start_text_x = 0.f; + S32 start_icon_x = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + start_text_x = icon_space + 1; + start_icon_x = 1; + break; + case LLFontGL::RIGHT: + start_text_x = (F32)getWidth(); + start_icon_x = getWidth() - mFont->getWidth(mText.getString()) - icon_space; + break; + case LLFontGL::HCENTER: + F32 center = (F32)getWidth()* 0.5f; + start_text_x = center + ((F32)icon_space * 0.5f); + start_icon_x = center - (((F32)icon_space + mFont->getWidth(mText.getString())) * 0.5f); + break; + } + mFont->render(mText.getWString(), 0, + start_text_x, 0.f, + display_color, + mFontAlignment, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + string_chars, + getTextWidth(), + &right_x, + true); + + if (mIcon) + { + mIcon->draw(start_icon_x, 0, icon_height, icon_height, mColor); + } +} + + diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h index c6c43537c6..c5d785ae52 100644 --- a/indra/llui/llscrolllistcell.h +++ b/indra/llui/llscrolllistcell.h @@ -1,280 +1,280 @@ -/** - * @file llscrolllistcell.h - * @brief Scroll lists are composed of rows (items), each of which - * contains columns (cells). - * - * $LicenseInfo:firstyear=2007&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 LLSCROLLLISTCELL_H -#define LLSCROLLLISTCELL_H - -#include "llfontgl.h" // HAlign -#include "llpointer.h" // LLPointer<> -#include "lluistring.h" -#include "v4color.h" -#include "llui.h" -#include "llgltexture.h" - -class LLCheckBoxCtrl; -class LLSD; -class LLUIImage; - -/* - * Represents a cell in a scrollable table. - * - * Sub-classes must return height and other properties - * though width accessors are implemented by the base class. - * It is therefore important for sub-class constructors to call - * setWidth() with realistic values. - */ -class LLScrollListCell -{ -public: - struct Params : public LLInitParam::Block - { - Optional type, - column; - - Optional width; - Optional enabled, - visible; - - Optional userdata; - Optional value; // state of checkbox, icon id/name, date - Optional alt_value; - Optional label; // description or text - Optional tool_tip; - - Optional font; - Optional font_color; - Optional font_halign; - - Optional color; - - Params() - : type("type", "text"), - column("column"), - width("width"), - enabled("enabled", true), - visible("visible", true), - value("value"), - alt_value("alt_value", ""), - label("label"), - tool_tip("tool_tip", ""), - font("font", LLFontGL::getFontEmojiSmall()), - font_color("font_color", LLColor4::black), - color("color", LLColor4::white), - font_halign("halign", LLFontGL::LEFT) - { - addSynonym(column, "name"); - addSynonym(font_color, "font-color"); - } - }; - - static LLScrollListCell* create(const Params&); - - LLScrollListCell(const LLScrollListCell::Params&); - virtual ~LLScrollListCell() {}; - - virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const {}; // truncate to given width, if possible - virtual S32 getWidth() const {return mWidth;} - virtual S32 getContentWidth() const { return 0; } - virtual S32 getHeight() const { return 0; } - virtual const LLSD getValue() const; - virtual const LLSD getAltValue() const; - virtual void setValue(const LLSD& value) { } - virtual void setAltValue(const LLSD& value) { } - virtual const std::string &getToolTip() const { return mToolTip; } - virtual void setToolTip(const std::string &str) { mToolTip = str; } - virtual bool getVisible() const { return true; } - virtual void setWidth(S32 width) { mWidth = width; } - virtual void highlightText(S32 offset, S32 num_chars) {} - virtual bool isText() const { return false; } - virtual bool needsToolTip() const { return ! mToolTip.empty(); } - virtual void setColor(const LLColor4&) {} - virtual void onCommit() {}; - - virtual bool handleClick() { return false; } - virtual void setEnabled(bool enable) { } - -private: - S32 mWidth; - std::string mToolTip; -}; - -class LLScrollListSpacer : public LLScrollListCell -{ -public: - LLScrollListSpacer(const LLScrollListCell::Params& p) : LLScrollListCell(p) {} - /*virtual*/ ~LLScrollListSpacer() {}; - /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const {} -}; - -/* - * Cell displaying a text label. - */ -class LLScrollListText : public LLScrollListCell -{ -public: - LLScrollListText(const LLScrollListCell::Params&); - /*virtual*/ ~LLScrollListText(); - - /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; - /*virtual*/ S32 getContentWidth() const; - /*virtual*/ S32 getHeight() const; - /*virtual*/ void setValue(const LLSD& value); - /*virtual*/ void setAltValue(const LLSD& value); - /*virtual*/ const LLSD getValue() const; - /*virtual*/ const LLSD getAltValue() const; - /*virtual*/ bool getVisible() const; - /*virtual*/ void highlightText(S32 offset, S32 num_chars); - - /*virtual*/ void setColor(const LLColor4&); - /*virtual*/ bool isText() const; - /*virtual*/ const std::string & getToolTip() const; - /*virtual*/ bool needsToolTip() const; - - S32 getTextWidth() const { return mTextWidth;} - void setTextWidth(S32 value) { mTextWidth = value;} - virtual void setWidth(S32 width) { LLScrollListCell::setWidth(width); mTextWidth = width; } - - void setText(const LLStringExplicit& text); - void setFontStyle(const U8 font_style); - void setAlignment(LLFontGL::HAlign align) { mFontAlignment = align; } - -protected: - LLUIString mText; - LLUIString mAltText; - S32 mTextWidth; - const LLFontGL* mFont; - LLColor4 mColor; - LLColor4 mHighlightColor; - U8 mUseColor; - LLFontGL::HAlign mFontAlignment; - bool mVisible; - S32 mHighlightCount; - S32 mHighlightOffset; - - LLPointer mRoundedRectImage; - - static U32 sCount; -}; - -/* - * Cell displaying an image. AT the moment, this is specifically UI image - */ -class LLScrollListIcon : public LLScrollListCell -{ -public: - LLScrollListIcon(const LLScrollListCell::Params& p); - /*virtual*/ ~LLScrollListIcon(); - /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; - /*virtual*/ S32 getWidth() const; - /*virtual*/ S32 getHeight() const; - /*virtual*/ const LLSD getValue() const; - /*virtual*/ void setColor(const LLColor4&); - /*virtual*/ void setValue(const LLSD& value); - -private: - LLPointer mIcon; - LLColor4 mColor; - LLFontGL::HAlign mAlignment; -}; - - -class LLScrollListBar : public LLScrollListCell -{ -public: - LLScrollListBar(const LLScrollListCell::Params& p); - /*virtual*/ ~LLScrollListBar(); - /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; - /*virtual*/ S32 getWidth() const; - /*virtual*/ S32 getHeight() const; - /*virtual*/ const LLSD getValue() const; - /*virtual*/ void setColor(const LLColor4&); - /*virtual*/ void setValue(const LLSD& value); - -private: - LLColor4 mColor; - F32 mRatio; - S32 mBottom; - S32 mRightPad; - S32 mLeftPad; -}; -/* - * An interactive cell containing a check box. - */ -class LLScrollListCheck : public LLScrollListCell -{ -public: - LLScrollListCheck( const LLScrollListCell::Params&); - /*virtual*/ ~LLScrollListCheck(); - /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; - /*virtual*/ S32 getHeight() const { return 0; } - /*virtual*/ const LLSD getValue() const; - /*virtual*/ void setValue(const LLSD& value); - /*virtual*/ void onCommit(); - - /*virtual*/ bool handleClick(); - /*virtual*/ void setEnabled(bool enable); - - LLCheckBoxCtrl* getCheckBox() { return mCheckBox; } - -private: - LLCheckBoxCtrl* mCheckBox; -}; - -class LLScrollListDate : public LLScrollListText -{ -public: - LLScrollListDate( const LLScrollListCell::Params& p ); - virtual void setValue(const LLSD& value); - virtual const LLSD getValue() const; - -private: - LLDate mDate; -}; - -/* -* Cell displaying icon and text. -*/ - -class LLScrollListIconText : public LLScrollListText -{ -public: - LLScrollListIconText(const LLScrollListCell::Params& p); - /*virtual*/ ~LLScrollListIconText(); - /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; - /*virtual*/ const LLSD getValue() const; - /*virtual*/ void setValue(const LLSD& value); - - - S32 getIconWidth() const; - /*virtual*/ void setWidth(S32 width);/* { LLScrollListCell::setWidth(width); mTextWidth = width - ; }*/ - -private: - LLPointer mIcon; - S32 mPad; -}; - -#endif +/** + * @file llscrolllistcell.h + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). + * + * $LicenseInfo:firstyear=2007&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 LLSCROLLLISTCELL_H +#define LLSCROLLLISTCELL_H + +#include "llfontgl.h" // HAlign +#include "llpointer.h" // LLPointer<> +#include "lluistring.h" +#include "v4color.h" +#include "llui.h" +#include "llgltexture.h" + +class LLCheckBoxCtrl; +class LLSD; +class LLUIImage; + +/* + * Represents a cell in a scrollable table. + * + * Sub-classes must return height and other properties + * though width accessors are implemented by the base class. + * It is therefore important for sub-class constructors to call + * setWidth() with realistic values. + */ +class LLScrollListCell +{ +public: + struct Params : public LLInitParam::Block + { + Optional type, + column; + + Optional width; + Optional enabled, + visible; + + Optional userdata; + Optional value; // state of checkbox, icon id/name, date + Optional alt_value; + Optional label; // description or text + Optional tool_tip; + + Optional font; + Optional font_color; + Optional font_halign; + + Optional color; + + Params() + : type("type", "text"), + column("column"), + width("width"), + enabled("enabled", true), + visible("visible", true), + value("value"), + alt_value("alt_value", ""), + label("label"), + tool_tip("tool_tip", ""), + font("font", LLFontGL::getFontEmojiSmall()), + font_color("font_color", LLColor4::black), + color("color", LLColor4::white), + font_halign("halign", LLFontGL::LEFT) + { + addSynonym(column, "name"); + addSynonym(font_color, "font-color"); + } + }; + + static LLScrollListCell* create(const Params&); + + LLScrollListCell(const LLScrollListCell::Params&); + virtual ~LLScrollListCell() {}; + + virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const {}; // truncate to given width, if possible + virtual S32 getWidth() const {return mWidth;} + virtual S32 getContentWidth() const { return 0; } + virtual S32 getHeight() const { return 0; } + virtual const LLSD getValue() const; + virtual const LLSD getAltValue() const; + virtual void setValue(const LLSD& value) { } + virtual void setAltValue(const LLSD& value) { } + virtual const std::string &getToolTip() const { return mToolTip; } + virtual void setToolTip(const std::string &str) { mToolTip = str; } + virtual bool getVisible() const { return true; } + virtual void setWidth(S32 width) { mWidth = width; } + virtual void highlightText(S32 offset, S32 num_chars) {} + virtual bool isText() const { return false; } + virtual bool needsToolTip() const { return ! mToolTip.empty(); } + virtual void setColor(const LLColor4&) {} + virtual void onCommit() {}; + + virtual bool handleClick() { return false; } + virtual void setEnabled(bool enable) { } + +private: + S32 mWidth; + std::string mToolTip; +}; + +class LLScrollListSpacer : public LLScrollListCell +{ +public: + LLScrollListSpacer(const LLScrollListCell::Params& p) : LLScrollListCell(p) {} + /*virtual*/ ~LLScrollListSpacer() {}; + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const {} +}; + +/* + * Cell displaying a text label. + */ +class LLScrollListText : public LLScrollListCell +{ +public: + LLScrollListText(const LLScrollListCell::Params&); + /*virtual*/ ~LLScrollListText(); + + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ S32 getContentWidth() const; + /*virtual*/ S32 getHeight() const; + /*virtual*/ void setValue(const LLSD& value); + /*virtual*/ void setAltValue(const LLSD& value); + /*virtual*/ const LLSD getValue() const; + /*virtual*/ const LLSD getAltValue() const; + /*virtual*/ bool getVisible() const; + /*virtual*/ void highlightText(S32 offset, S32 num_chars); + + /*virtual*/ void setColor(const LLColor4&); + /*virtual*/ bool isText() const; + /*virtual*/ const std::string & getToolTip() const; + /*virtual*/ bool needsToolTip() const; + + S32 getTextWidth() const { return mTextWidth;} + void setTextWidth(S32 value) { mTextWidth = value;} + virtual void setWidth(S32 width) { LLScrollListCell::setWidth(width); mTextWidth = width; } + + void setText(const LLStringExplicit& text); + void setFontStyle(const U8 font_style); + void setAlignment(LLFontGL::HAlign align) { mFontAlignment = align; } + +protected: + LLUIString mText; + LLUIString mAltText; + S32 mTextWidth; + const LLFontGL* mFont; + LLColor4 mColor; + LLColor4 mHighlightColor; + U8 mUseColor; + LLFontGL::HAlign mFontAlignment; + bool mVisible; + S32 mHighlightCount; + S32 mHighlightOffset; + + LLPointer mRoundedRectImage; + + static U32 sCount; +}; + +/* + * Cell displaying an image. AT the moment, this is specifically UI image + */ +class LLScrollListIcon : public LLScrollListCell +{ +public: + LLScrollListIcon(const LLScrollListCell::Params& p); + /*virtual*/ ~LLScrollListIcon(); + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ S32 getWidth() const; + /*virtual*/ S32 getHeight() const; + /*virtual*/ const LLSD getValue() const; + /*virtual*/ void setColor(const LLColor4&); + /*virtual*/ void setValue(const LLSD& value); + +private: + LLPointer mIcon; + LLColor4 mColor; + LLFontGL::HAlign mAlignment; +}; + + +class LLScrollListBar : public LLScrollListCell +{ +public: + LLScrollListBar(const LLScrollListCell::Params& p); + /*virtual*/ ~LLScrollListBar(); + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ S32 getWidth() const; + /*virtual*/ S32 getHeight() const; + /*virtual*/ const LLSD getValue() const; + /*virtual*/ void setColor(const LLColor4&); + /*virtual*/ void setValue(const LLSD& value); + +private: + LLColor4 mColor; + F32 mRatio; + S32 mBottom; + S32 mRightPad; + S32 mLeftPad; +}; +/* + * An interactive cell containing a check box. + */ +class LLScrollListCheck : public LLScrollListCell +{ +public: + LLScrollListCheck( const LLScrollListCell::Params&); + /*virtual*/ ~LLScrollListCheck(); + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ S32 getHeight() const { return 0; } + /*virtual*/ const LLSD getValue() const; + /*virtual*/ void setValue(const LLSD& value); + /*virtual*/ void onCommit(); + + /*virtual*/ bool handleClick(); + /*virtual*/ void setEnabled(bool enable); + + LLCheckBoxCtrl* getCheckBox() { return mCheckBox; } + +private: + LLCheckBoxCtrl* mCheckBox; +}; + +class LLScrollListDate : public LLScrollListText +{ +public: + LLScrollListDate( const LLScrollListCell::Params& p ); + virtual void setValue(const LLSD& value); + virtual const LLSD getValue() const; + +private: + LLDate mDate; +}; + +/* +* Cell displaying icon and text. +*/ + +class LLScrollListIconText : public LLScrollListText +{ +public: + LLScrollListIconText(const LLScrollListCell::Params& p); + /*virtual*/ ~LLScrollListIconText(); + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ const LLSD getValue() const; + /*virtual*/ void setValue(const LLSD& value); + + + S32 getIconWidth() const; + /*virtual*/ void setWidth(S32 width);/* { LLScrollListCell::setWidth(width); mTextWidth = width - ; }*/ + +private: + LLPointer mIcon; + S32 mPad; +}; + +#endif diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp index c6be401676..a4510d1fc2 100644 --- a/indra/llui/llscrolllistcolumn.cpp +++ b/indra/llui/llscrolllistcolumn.cpp @@ -1,340 +1,340 @@ -/** - * @file llscrollcolumnheader.cpp - * @brief Scroll lists are composed of rows (items), each of which - * contains columns (cells). - * - * $LicenseInfo:firstyear=2007&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 "llscrolllistcolumn.h" - -#include "llbutton.h" -#include "llresizebar.h" -#include "llscrolllistcell.h" -#include "llscrolllistctrl.h" -#include "llscrolllistitem.h" -#include "lluictrlfactory.h" - -const S32 MIN_COLUMN_WIDTH = 20; - -// defaults for LLScrollColumnHeader param block pulled from widgets/scroll_column_header.xml -static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid(LLScrollColumnHeader::Params), "scroll_column_header"); - -//--------------------------------------------------------------------------- -// LLScrollColumnHeader -//--------------------------------------------------------------------------- -LLScrollColumnHeader::Params::Params() -: column("column") -{} - - -LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p) -: LLButton(p), // use combobox params to steal images - mColumn(p.column), - mHasResizableElement(false) -{ - setClickedCallback(boost::bind(&LLScrollColumnHeader::onClick, this, _2)); - - // resize handles on left and right - const S32 RESIZE_BAR_THICKNESS = 3; - LLResizeBar::Params resize_bar_p; - resize_bar_p.resizing_view(this); - resize_bar_p.rect(LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0)); - resize_bar_p.min_size(MIN_COLUMN_WIDTH); - resize_bar_p.side(LLResizeBar::RIGHT); - resize_bar_p.enabled(false); - mResizeBar = LLUICtrlFactory::create(resize_bar_p); - addChild(mResizeBar); -} - -LLScrollColumnHeader::~LLScrollColumnHeader() -{} - -void LLScrollColumnHeader::draw() -{ - std::string sort_column = mColumn->mParentCtrl->getSortColumnName(); - bool draw_arrow = !mColumn->mLabel.empty() - && mColumn->mParentCtrl->isSorted() - // check for indirect sorting column as well as column's sorting name - && (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName); - - bool is_ascending = mColumn->mParentCtrl->getSortAscending(); - if (draw_arrow) - { - setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white); - } - else - { - setImageOverlay(LLUUID::null); - } - - // Draw children - LLButton::draw(); -} - -bool LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - if (canResize() && mResizeBar->getRect().pointInRect(x, y)) - { - // reshape column to max content width - mColumn->mParentCtrl->calcMaxContentWidth(); - LLRect column_rect = getRect(); - column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth; - setShape(column_rect, true); - } - else - { - onClick(LLSD()); - } - return true; -} - -void LLScrollColumnHeader::onClick(const LLSD& data) -{ - if (mColumn) - { - LLScrollListCtrl::onClickColumn(mColumn); - } -} - -LLView* LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) -{ - // this logic assumes dragging on right - llassert(snap_edge == SNAP_RIGHT); - - // use higher snap threshold for column headers - threshold = llmin(threshold, 10); - - LLRect snap_rect = getSnapRect(); - - mColumn->mParentCtrl->calcMaxContentWidth(); - - S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth(); - - // x coord growing means column growing, so same signs mean we're going in right direction - if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) - { - new_edge_val = snap_rect.mRight + snap_delta; - } - else - { - LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1); - while (next_column) - { - if (next_column->mHeader) - { - snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight; - if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) - { - new_edge_val = snap_rect.mRight + snap_delta; - } - break; - } - next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1); - } - } - - return this; -} - -void LLScrollColumnHeader::handleReshape(const LLRect& new_rect, bool by_user) -{ - S32 new_width = new_rect.getWidth(); - S32 delta_width = new_width - (getRect().getWidth() /*+ mColumn->mParentCtrl->getColumnPadding()*/); - - if (delta_width != 0) - { - S32 remaining_width = -delta_width; - S32 col; - for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++) - { - LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); - if (!columnp) continue; - - if (columnp->mHeader && columnp->mHeader->canResize()) - { - // how many pixels in width can this column afford to give up? - S32 resize_buffer_amt = llmax(0, columnp->getWidth() - MIN_COLUMN_WIDTH); - - // user shrinking column, need to add width to other columns - if (delta_width < 0) - { - if (columnp->getWidth() > 0) - { - // statically sized column, give all remaining width to this column - columnp->setWidth(columnp->getWidth() + remaining_width); - if (columnp->mRelWidth > 0.f) - { - columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); - } - // all padding went to this widget, we're done - break; - } - } - else - { - // user growing column, need to take width from other columns - remaining_width += resize_buffer_amt; - - if (columnp->getWidth() > 0) - { - columnp->setWidth(columnp->getWidth() - llmin(columnp->getWidth() - MIN_COLUMN_WIDTH, delta_width)); - if (columnp->mRelWidth > 0.f) - { - columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); - } - } - - if (remaining_width >= 0) - { - // width sucked up from neighboring columns, done - break; - } - } - } - } - - // clamp resize amount to maximum that can be absorbed by other columns - if (delta_width > 0) - { - delta_width += llmin(remaining_width, 0); - } - - // propagate constrained delta_width to new width for this column - new_width = getRect().getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding(); - - // use requested width - mColumn->setWidth(new_width); - - // update proportional spacing - if (mColumn->mRelWidth > 0.f) - { - mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); - } - - // tell scroll list to layout columns again - // do immediate update to get proper feedback to resize handle - // which needs to know how far the resize actually went - const bool force_update = true; - mColumn->mParentCtrl->updateColumns(force_update); - } -} - -void LLScrollColumnHeader::setHasResizableElement(bool resizable) -{ - if (mHasResizableElement != resizable) - { - mColumn->mParentCtrl->dirtyColumns(); - mHasResizableElement = resizable; - } -} - -void LLScrollColumnHeader::updateResizeBars() -{ - S32 num_resizable_columns = 0; - S32 col; - for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) - { - LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); - if (columnp && columnp->mHeader && columnp->mHeader->canResize()) - { - num_resizable_columns++; - } - } - - S32 num_resizers_enabled = 0; - - // now enable/disable resize handles on resizable columns if we have at least two - for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) - { - LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); - if (!columnp || !columnp->mHeader) continue; - bool enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize(); - columnp->mHeader->enableResizeBar(enable); - if (enable) - { - num_resizers_enabled++; - } - } -} - -void LLScrollColumnHeader::enableResizeBar(bool enable) -{ - mResizeBar->setEnabled(enable); -} - -bool LLScrollColumnHeader::canResize() -{ - return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth); -} - -void LLScrollListColumn::SortNames::declareValues() -{ - declare("ascending", LLScrollListColumn::ASCENDING); - declare("descending", LLScrollListColumn::DESCENDING); -} - -// -// LLScrollListColumn -// -//static -const LLScrollListColumn::Params& LLScrollListColumn::getDefaultParams() -{ - return LLUICtrlFactory::getDefaultParams(); -} - - -LLScrollListColumn::LLScrollListColumn(const Params& p, LLScrollListCtrl* parent) -: mWidth(0), - mIndex (-1), - mParentCtrl(parent), - mName(p.name), - mLabel(p.header.label), - mHeader(NULL), - mMaxContentWidth(0), - mDynamicWidth(p.width.dynamic_width), - mRelWidth(p.width.relative_width), - mFontAlignment(p.halign), - mSortingColumn(p.sort_column) -{ - if (p.sort_ascending.isProvided()) - { - mSortDirection = p.sort_ascending() ? ASCENDING : DESCENDING; - } - else - { - mSortDirection = p.sort_direction; - } - - setWidth(p.width.pixel_width); -} - -void LLScrollListColumn::setWidth(S32 width) -{ - if (!mDynamicWidth && mRelWidth <= 0.f) - { - mParentCtrl->updateStaticColumnWidth(this, width); - } - mWidth = width; -} +/** + * @file llscrollcolumnheader.cpp + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). + * + * $LicenseInfo:firstyear=2007&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 "llscrolllistcolumn.h" + +#include "llbutton.h" +#include "llresizebar.h" +#include "llscrolllistcell.h" +#include "llscrolllistctrl.h" +#include "llscrolllistitem.h" +#include "lluictrlfactory.h" + +const S32 MIN_COLUMN_WIDTH = 20; + +// defaults for LLScrollColumnHeader param block pulled from widgets/scroll_column_header.xml +static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid(LLScrollColumnHeader::Params), "scroll_column_header"); + +//--------------------------------------------------------------------------- +// LLScrollColumnHeader +//--------------------------------------------------------------------------- +LLScrollColumnHeader::Params::Params() +: column("column") +{} + + +LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p) +: LLButton(p), // use combobox params to steal images + mColumn(p.column), + mHasResizableElement(false) +{ + setClickedCallback(boost::bind(&LLScrollColumnHeader::onClick, this, _2)); + + // resize handles on left and right + const S32 RESIZE_BAR_THICKNESS = 3; + LLResizeBar::Params resize_bar_p; + resize_bar_p.resizing_view(this); + resize_bar_p.rect(LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0)); + resize_bar_p.min_size(MIN_COLUMN_WIDTH); + resize_bar_p.side(LLResizeBar::RIGHT); + resize_bar_p.enabled(false); + mResizeBar = LLUICtrlFactory::create(resize_bar_p); + addChild(mResizeBar); +} + +LLScrollColumnHeader::~LLScrollColumnHeader() +{} + +void LLScrollColumnHeader::draw() +{ + std::string sort_column = mColumn->mParentCtrl->getSortColumnName(); + bool draw_arrow = !mColumn->mLabel.empty() + && mColumn->mParentCtrl->isSorted() + // check for indirect sorting column as well as column's sorting name + && (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName); + + bool is_ascending = mColumn->mParentCtrl->getSortAscending(); + if (draw_arrow) + { + setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white); + } + else + { + setImageOverlay(LLUUID::null); + } + + // Draw children + LLButton::draw(); +} + +bool LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + if (canResize() && mResizeBar->getRect().pointInRect(x, y)) + { + // reshape column to max content width + mColumn->mParentCtrl->calcMaxContentWidth(); + LLRect column_rect = getRect(); + column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth; + setShape(column_rect, true); + } + else + { + onClick(LLSD()); + } + return true; +} + +void LLScrollColumnHeader::onClick(const LLSD& data) +{ + if (mColumn) + { + LLScrollListCtrl::onClickColumn(mColumn); + } +} + +LLView* LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) +{ + // this logic assumes dragging on right + llassert(snap_edge == SNAP_RIGHT); + + // use higher snap threshold for column headers + threshold = llmin(threshold, 10); + + LLRect snap_rect = getSnapRect(); + + mColumn->mParentCtrl->calcMaxContentWidth(); + + S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth(); + + // x coord growing means column growing, so same signs mean we're going in right direction + if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) + { + new_edge_val = snap_rect.mRight + snap_delta; + } + else + { + LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1); + while (next_column) + { + if (next_column->mHeader) + { + snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight; + if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) + { + new_edge_val = snap_rect.mRight + snap_delta; + } + break; + } + next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1); + } + } + + return this; +} + +void LLScrollColumnHeader::handleReshape(const LLRect& new_rect, bool by_user) +{ + S32 new_width = new_rect.getWidth(); + S32 delta_width = new_width - (getRect().getWidth() /*+ mColumn->mParentCtrl->getColumnPadding()*/); + + if (delta_width != 0) + { + S32 remaining_width = -delta_width; + S32 col; + for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++) + { + LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); + if (!columnp) continue; + + if (columnp->mHeader && columnp->mHeader->canResize()) + { + // how many pixels in width can this column afford to give up? + S32 resize_buffer_amt = llmax(0, columnp->getWidth() - MIN_COLUMN_WIDTH); + + // user shrinking column, need to add width to other columns + if (delta_width < 0) + { + if (columnp->getWidth() > 0) + { + // statically sized column, give all remaining width to this column + columnp->setWidth(columnp->getWidth() + remaining_width); + if (columnp->mRelWidth > 0.f) + { + columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); + } + // all padding went to this widget, we're done + break; + } + } + else + { + // user growing column, need to take width from other columns + remaining_width += resize_buffer_amt; + + if (columnp->getWidth() > 0) + { + columnp->setWidth(columnp->getWidth() - llmin(columnp->getWidth() - MIN_COLUMN_WIDTH, delta_width)); + if (columnp->mRelWidth > 0.f) + { + columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); + } + } + + if (remaining_width >= 0) + { + // width sucked up from neighboring columns, done + break; + } + } + } + } + + // clamp resize amount to maximum that can be absorbed by other columns + if (delta_width > 0) + { + delta_width += llmin(remaining_width, 0); + } + + // propagate constrained delta_width to new width for this column + new_width = getRect().getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding(); + + // use requested width + mColumn->setWidth(new_width); + + // update proportional spacing + if (mColumn->mRelWidth > 0.f) + { + mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); + } + + // tell scroll list to layout columns again + // do immediate update to get proper feedback to resize handle + // which needs to know how far the resize actually went + const bool force_update = true; + mColumn->mParentCtrl->updateColumns(force_update); + } +} + +void LLScrollColumnHeader::setHasResizableElement(bool resizable) +{ + if (mHasResizableElement != resizable) + { + mColumn->mParentCtrl->dirtyColumns(); + mHasResizableElement = resizable; + } +} + +void LLScrollColumnHeader::updateResizeBars() +{ + S32 num_resizable_columns = 0; + S32 col; + for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) + { + LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); + if (columnp && columnp->mHeader && columnp->mHeader->canResize()) + { + num_resizable_columns++; + } + } + + S32 num_resizers_enabled = 0; + + // now enable/disable resize handles on resizable columns if we have at least two + for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) + { + LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); + if (!columnp || !columnp->mHeader) continue; + bool enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize(); + columnp->mHeader->enableResizeBar(enable); + if (enable) + { + num_resizers_enabled++; + } + } +} + +void LLScrollColumnHeader::enableResizeBar(bool enable) +{ + mResizeBar->setEnabled(enable); +} + +bool LLScrollColumnHeader::canResize() +{ + return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth); +} + +void LLScrollListColumn::SortNames::declareValues() +{ + declare("ascending", LLScrollListColumn::ASCENDING); + declare("descending", LLScrollListColumn::DESCENDING); +} + +// +// LLScrollListColumn +// +//static +const LLScrollListColumn::Params& LLScrollListColumn::getDefaultParams() +{ + return LLUICtrlFactory::getDefaultParams(); +} + + +LLScrollListColumn::LLScrollListColumn(const Params& p, LLScrollListCtrl* parent) +: mWidth(0), + mIndex (-1), + mParentCtrl(parent), + mName(p.name), + mLabel(p.header.label), + mHeader(NULL), + mMaxContentWidth(0), + mDynamicWidth(p.width.dynamic_width), + mRelWidth(p.width.relative_width), + mFontAlignment(p.halign), + mSortingColumn(p.sort_column) +{ + if (p.sort_ascending.isProvided()) + { + mSortDirection = p.sort_ascending() ? ASCENDING : DESCENDING; + } + else + { + mSortDirection = p.sort_direction; + } + + setWidth(p.width.pixel_width); +} + +void LLScrollListColumn::setWidth(S32 width) +{ + if (!mDynamicWidth && mRelWidth <= 0.f) + { + mParentCtrl->updateStaticColumnWidth(this, width); + } + mWidth = width; +} diff --git a/indra/llui/llscrolllistcolumn.h b/indra/llui/llscrolllistcolumn.h index 042e9e5f02..72dfcdafe0 100644 --- a/indra/llui/llscrolllistcolumn.h +++ b/indra/llui/llscrolllistcolumn.h @@ -1,172 +1,172 @@ -/** - * @file llscrollcolumnheader.h - * @brief Scroll lists are composed of rows (items), each of which - * contains columns (cells). - * - * $LicenseInfo:firstyear=2007&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 LLSCROLLLISTCOLUMN_H -#define LLSCROLLLISTCOLUMN_H - -#include "llrect.h" -#include "lluistring.h" -#include "llbutton.h" -#include "llinitparam.h" - -class LLScrollListColumn; -class LLResizeBar; -class LLScrollListCtrl; - -class LLScrollColumnHeader : public LLButton -{ -public: - struct Params : public LLInitParam::Block - { - Mandatory column; - - Params(); - }; - LLScrollColumnHeader(const Params&); - ~LLScrollColumnHeader(); - - /*virtual*/ void draw(); - /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); - - /*virtual*/ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding); - /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); - - LLScrollListColumn* getColumn() { return mColumn; } - void setHasResizableElement(bool resizable); - void updateResizeBars(); - bool canResize(); - void enableResizeBar(bool enable); - - void onClick(const LLSD& data); - -private: - LLScrollListColumn* mColumn; - LLResizeBar* mResizeBar; - bool mHasResizableElement; -}; - -/* - * A simple data class describing a column within a scroll list. - */ -class LLScrollListColumn -{ -public: - typedef enum e_sort_direction - { - DESCENDING, - ASCENDING - } ESortDirection; - - struct SortNames - : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - struct Params : public LLInitParam::Block - { - Optional name, - tool_tip; - Optional sort_column; - Optional sort_direction; - Optional sort_ascending; - - struct Width : public LLInitParam::ChoiceBlock - { - Alternative dynamic_width; - Alternative pixel_width; - Alternative relative_width; - - Width() - : dynamic_width("dynamic_width", false), - pixel_width("width"), - relative_width("relative_width", -1.f) - { - addSynonym(relative_width, "relwidth"); - } - }; - Optional width; - - // either an image or label is used in column header - struct Header : public LLInitParam::ChoiceBlock
- { - Alternative label; - Alternative image; - - Header() - : label("label"), - image("image") - {} - }; - Optional
header; - - Optional halign; - - Params() - : name("name"), - tool_tip("tool_tip"), - sort_column("sort_column"), - sort_direction("sort_direction"), - sort_ascending("sort_ascending", true), - halign("halign", LLFontGL::LEFT) - { - // default choice to "dynamic_width" - changeDefault(width.dynamic_width, true); - - addSynonym(sort_column, "sort"); - } - }; - - static const Params& getDefaultParams(); - - //NOTE: this is default constructible so we can store it in a map. - LLScrollListColumn(const Params& p = getDefaultParams(), LLScrollListCtrl* = NULL); - - void setWidth(S32 width); - S32 getWidth() const { return mWidth; } - -public: - // Public data is fine so long as this remains a simple struct-like data class. - // If it ever gets any smarter than that, these should all become private - // with protected or public accessor methods added as needed. -MG - std::string mName; - std::string mSortingColumn; - ESortDirection mSortDirection; - LLUIString mLabel; - F32 mRelWidth; - bool mDynamicWidth; - S32 mMaxContentWidth; - S32 mIndex; - LLScrollListCtrl* mParentCtrl; - LLScrollColumnHeader* mHeader; - LLFontGL::HAlign mFontAlignment; - -private: - S32 mWidth; -}; - -#endif +/** + * @file llscrollcolumnheader.h + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). + * + * $LicenseInfo:firstyear=2007&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 LLSCROLLLISTCOLUMN_H +#define LLSCROLLLISTCOLUMN_H + +#include "llrect.h" +#include "lluistring.h" +#include "llbutton.h" +#include "llinitparam.h" + +class LLScrollListColumn; +class LLResizeBar; +class LLScrollListCtrl; + +class LLScrollColumnHeader : public LLButton +{ +public: + struct Params : public LLInitParam::Block + { + Mandatory column; + + Params(); + }; + LLScrollColumnHeader(const Params&); + ~LLScrollColumnHeader(); + + /*virtual*/ void draw(); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + + /*virtual*/ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding); + /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); + + LLScrollListColumn* getColumn() { return mColumn; } + void setHasResizableElement(bool resizable); + void updateResizeBars(); + bool canResize(); + void enableResizeBar(bool enable); + + void onClick(const LLSD& data); + +private: + LLScrollListColumn* mColumn; + LLResizeBar* mResizeBar; + bool mHasResizableElement; +}; + +/* + * A simple data class describing a column within a scroll list. + */ +class LLScrollListColumn +{ +public: + typedef enum e_sort_direction + { + DESCENDING, + ASCENDING + } ESortDirection; + + struct SortNames + : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + struct Params : public LLInitParam::Block + { + Optional name, + tool_tip; + Optional sort_column; + Optional sort_direction; + Optional sort_ascending; + + struct Width : public LLInitParam::ChoiceBlock + { + Alternative dynamic_width; + Alternative pixel_width; + Alternative relative_width; + + Width() + : dynamic_width("dynamic_width", false), + pixel_width("width"), + relative_width("relative_width", -1.f) + { + addSynonym(relative_width, "relwidth"); + } + }; + Optional width; + + // either an image or label is used in column header + struct Header : public LLInitParam::ChoiceBlock
+ { + Alternative label; + Alternative image; + + Header() + : label("label"), + image("image") + {} + }; + Optional
header; + + Optional halign; + + Params() + : name("name"), + tool_tip("tool_tip"), + sort_column("sort_column"), + sort_direction("sort_direction"), + sort_ascending("sort_ascending", true), + halign("halign", LLFontGL::LEFT) + { + // default choice to "dynamic_width" + changeDefault(width.dynamic_width, true); + + addSynonym(sort_column, "sort"); + } + }; + + static const Params& getDefaultParams(); + + //NOTE: this is default constructible so we can store it in a map. + LLScrollListColumn(const Params& p = getDefaultParams(), LLScrollListCtrl* = NULL); + + void setWidth(S32 width); + S32 getWidth() const { return mWidth; } + +public: + // Public data is fine so long as this remains a simple struct-like data class. + // If it ever gets any smarter than that, these should all become private + // with protected or public accessor methods added as needed. -MG + std::string mName; + std::string mSortingColumn; + ESortDirection mSortDirection; + LLUIString mLabel; + F32 mRelWidth; + bool mDynamicWidth; + S32 mMaxContentWidth; + S32 mIndex; + LLScrollListCtrl* mParentCtrl; + LLScrollColumnHeader* mHeader; + LLFontGL::HAlign mFontAlignment; + +private: + S32 mWidth; +}; + +#endif diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 1868ac9639..39e575173d 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1,3448 +1,3448 @@ - /** - * @file llscrolllistctrl.cpp - * @brief Scroll lists are composed of rows (items), each of which - * contains columns (cells). - * - * $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 "linden_common.h" - -#include "llscrolllistctrl.h" - -#include - -#include "llstl.h" -#include "llboost.h" -//#include "indra_constants.h" - -#include "llavatarnamecache.h" -#include "llcheckboxctrl.h" -#include "llclipboard.h" -#include "llfocusmgr.h" -#include "llgl.h" // LLGLSUIDefault() -#include "lllocalcliprect.h" -//#include "llrender.h" -#include "llresmgr.h" -#include "llscrollbar.h" -#include "llscrolllistcell.h" -#include "llstring.h" -#include "llui.h" -#include "lluictrlfactory.h" -#include "llwindow.h" -#include "llcontrol.h" -#include "llkeyboard.h" -#include "llviewborder.h" -#include "lltextbox.h" -#include "llsdparam.h" -#include "llcachename.h" -#include "llmenugl.h" -#include "llurlaction.h" -#include "lltooltip.h" - -#include - -static LLDefaultChildRegistry::Register r("scroll_list"); - -// local structures & classes. -struct SortScrollListItem -{ - SortScrollListItem(const std::vector >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal, bool alternate_sort) - : mSortOrders(sort_orders) - , mSortSignal(sort_signal) - , mAltSort(alternate_sort) - {} - - bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) - { - // sort over all columns in order specified by mSortOrders - S32 sort_result = 0; - for (sort_order_t::const_reverse_iterator it = mSortOrders.rbegin(); - it != mSortOrders.rend(); ++it) - { - S32 col_idx = it->first; - bool sort_ascending = it->second; - - S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column? - - const LLScrollListCell *cell1 = i1->getColumn(col_idx); - const LLScrollListCell *cell2 = i2->getColumn(col_idx); - if (cell1 && cell2) - { - if(mSortSignal) - { - sort_result = order * (*mSortSignal)(col_idx,i1, i2); - } - else - { - if (mAltSort && !cell1->getAltValue().asString().empty() && !cell2->getAltValue().asString().empty()) - { - sort_result = order * LLStringUtil::compareDict(cell1->getAltValue().asString(), cell2->getAltValue().asString()); - } - else - { - sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString()); - } - } - if (sort_result != 0) - { - break; // we have a sort order! - } - } - } - - return sort_result < 0; - } - - - typedef std::vector > sort_order_t; - const LLScrollListCtrl::sort_signal_t* mSortSignal; - const sort_order_t& mSortOrders; - const bool mAltSort; -}; - -//--------------------------------------------------------------------------- -// LLScrollListCtrl -//--------------------------------------------------------------------------- - -void LLScrollListCtrl::SelectionTypeNames::declareValues() -{ - declare("row", LLScrollListCtrl::ROW); - declare("cell", LLScrollListCtrl::CELL); - declare("header", LLScrollListCtrl::HEADER); -} - -LLScrollListCtrl::Contents::Contents() -: columns("column"), - rows("row") -{ - addSynonym(columns, "columns"); - addSynonym(rows, "rows"); -} - -LLScrollListCtrl::Params::Params() -: multi_select("multi_select", false), - has_border("draw_border"), - draw_heading("draw_heading"), - search_column("search_column", 0), - selection_type("selection_type", ROW), - sort_column("sort_column", -1), - sort_ascending("sort_ascending", true), - can_sort("can_sort", true), - mouse_wheel_opaque("mouse_wheel_opaque", false), - commit_on_keyboard_movement("commit_on_keyboard_movement", true), - commit_on_selection_change("commit_on_selection_change", false), - heading_height("heading_height"), - page_lines("page_lines", 0), - background_visible("background_visible"), - draw_stripes("draw_stripes"), - column_padding("column_padding"), - row_padding("row_padding", 2), - fg_unselected_color("fg_unselected_color"), - fg_selected_color("fg_selected_color"), - bg_selected_color("bg_selected_color"), - fg_disable_color("fg_disable_color"), - bg_writeable_color("bg_writeable_color"), - bg_readonly_color("bg_readonly_color"), - bg_stripe_color("bg_stripe_color"), - hovered_color("hovered_color"), - highlighted_color("highlighted_color"), - contents(""), - scroll_bar_bg_visible("scroll_bar_bg_visible"), - scroll_bar_bg_color("scroll_bar_bg_color"), - border("border") -{} - -LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) -: LLUICtrl(p), - mLineHeight(0), - mScrollLines(0), - mMouseWheelOpaque(p.mouse_wheel_opaque), - mPageLines(p.page_lines), - mMaxSelectable(0), - mAllowKeyboardMovement(true), - mCommitOnKeyboardMovement(p.commit_on_keyboard_movement), - mCommitOnSelectionChange(p.commit_on_selection_change), - mSelectionChanged(false), - mSelectionType(p.selection_type), - mNeedsScroll(false), - mCanSelect(true), - mCanSort(p.can_sort), - mColumnsDirty(false), - mMaxItemCount(INT_MAX), - mBorderThickness( 2 ), - mOnDoubleClickCallback( NULL ), - mOnMaximumSelectCallback( NULL ), - mOnSortChangedCallback( NULL ), - mHighlightedItem(-1), - mBorder(NULL), - mSortCallback(NULL), - mCommentTextView(NULL), - mNumDynamicWidthColumns(0), - mTotalStaticColumnWidth(0), - mTotalColumnPadding(0), - mSorted(false), - mDirty(false), - mOriginalSelection(-1), - mLastSelected(NULL), - mHeadingHeight(p.heading_height), - mAllowMultipleSelection(p.multi_select), - mDisplayColumnHeaders(p.draw_heading), - mBackgroundVisible(p.background_visible), - mDrawStripes(p.draw_stripes), - mBgWriteableColor(p.bg_writeable_color()), - mBgReadOnlyColor(p.bg_readonly_color()), - mBgSelectedColor(p.bg_selected_color()), - mBgStripeColor(p.bg_stripe_color()), - mFgSelectedColor(p.fg_selected_color()), - mFgUnselectedColor(p.fg_unselected_color()), - mFgDisabledColor(p.fg_disable_color()), - mHighlightedColor(p.highlighted_color()), - mHoveredColor(p.hovered_color()), - mSearchColumn(p.search_column), - mColumnPadding(p.column_padding), - mRowPadding(p.row_padding), - mAlternateSort(false), - mContextMenuType(MENU_NONE), - mIsFriendSignal(NULL) -{ - mItemListRect.setOriginAndSize( - mBorderThickness, - mBorderThickness, - getRect().getWidth() - 2 * mBorderThickness, - getRect().getHeight() - 2 * mBorderThickness ); - - updateLineHeight(); - - // Init the scrollbar - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - LLRect scroll_rect; - scroll_rect.setOriginAndSize( - getRect().getWidth() - mBorderThickness - scrollbar_size, - mItemListRect.mBottom, - scrollbar_size, - mItemListRect.getHeight()); - - LLScrollbar::Params sbparams; - sbparams.name("Scrollbar"); - sbparams.rect(scroll_rect); - sbparams.orientation(LLScrollbar::VERTICAL); - sbparams.doc_size(getItemCount()); - sbparams.doc_pos(mScrollLines); - sbparams.page_size( getLinesPerPage() ); - sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2)); - sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); - sbparams.visible(false); - sbparams.bg_visible(p.scroll_bar_bg_visible); - sbparams.bg_color(p.scroll_bar_bg_color); - mScrollbar = LLUICtrlFactory::create (sbparams); - addChild(mScrollbar); - - // Border - if (p.has_border) - { - LLRect border_rect = getLocalRect(); - LLViewBorder::Params params = p.border; - params.rect(border_rect); - mBorder = LLUICtrlFactory::create (params); - addChild(mBorder); - } - - // set border *after* rect is fully initialized - if (mBorder) - { - mBorder->setRect(getLocalRect()); - mBorder->reshape(getRect().getWidth(), getRect().getHeight()); - } - - if (p.sort_column >= 0) - { - sortByColumnIndex(p.sort_column, p.sort_ascending); - } - - - for (LLInitParam::ParamIterator::const_iterator row_it = p.contents.columns.begin(); - row_it != p.contents.columns.end(); - ++row_it) - { - addColumn(*row_it); - } - - for (LLInitParam::ParamIterator::const_iterator row_it = p.contents.rows.begin(); - row_it != p.contents.rows.end(); - ++row_it) - { - addRow(*row_it); - } - - LLTextBox::Params text_p; - text_p.name("comment_text"); - text_p.border_visible(false); - text_p.rect(mItemListRect); - text_p.follows.flags(FOLLOWS_ALL); - // word wrap was added accroding to the EXT-6841 - text_p.wrap(true); - addChild(LLUICtrlFactory::create(text_p)); -} - -S32 LLScrollListCtrl::getSearchColumn() -{ - // search for proper search column - if (mSearchColumn < 0) - { - LLScrollListItem* itemp = getFirstData(); - if (itemp) - { - for(S32 column = 0; column < getNumColumns(); column++) - { - LLScrollListCell* cell = itemp->getColumn(column); - if (cell && cell->isText()) - { - mSearchColumn = column; - break; - } - } - } - } - return llclamp(mSearchColumn, 0, getNumColumns()); -} -/*virtual*/ -bool LLScrollListCtrl::preProcessChildNode(LLXMLNodePtr child) -{ - if (child->hasName("column") || child->hasName("row")) - { - return true; // skip - } - else - { - return false; - } -} - -LLScrollListCtrl::~LLScrollListCtrl() -{ - delete mSortCallback; - - std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); - mItemList.clear(); - clearColumns(); //clears columns and deletes headers - delete mIsFriendSignal; - - auto menu = mPopupMenuHandle.get(); - if (menu) - { - menu->die(); - mPopupMenuHandle.markDead(); - } -} - - -bool LLScrollListCtrl::setMaxItemCount(S32 max_count) -{ - if (max_count >= getItemCount()) - { - mMaxItemCount = max_count; - } - return (max_count == mMaxItemCount); -} - -S32 LLScrollListCtrl::isEmpty() const -{ - return mItemList.empty(); -} - -S32 LLScrollListCtrl::getItemCount() const -{ - return mItemList.size(); -} - -bool LLScrollListCtrl::hasSelectedItem() const -{ - item_list::iterator iter; - for (iter = mItemList.begin(); iter < mItemList.end(); ) - { - LLScrollListItem* itemp = *iter; - if (itemp && itemp->getSelected()) - { - return true; - } - iter++; - } - return false; -} - -// virtual LLScrolListInterface function (was deleteAllItems) -void LLScrollListCtrl::clearRows() -{ - std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); - mItemList.clear(); - //mItemCount = 0; - - // Scroll the bar back up to the top. - mScrollbar->setDocParams(0, 0); - - mScrollLines = 0; - mLastSelected = NULL; - updateLayout(); - mDirty = false; -} - - -LLScrollListItem* LLScrollListCtrl::getFirstSelected() const -{ - item_list::const_iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - if (item->getSelected()) - { - return item; - } - } - return NULL; -} - -std::vector LLScrollListCtrl::getAllSelected() const -{ - std::vector ret; - item_list::const_iterator iter; - for(iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - if (item->getSelected()) - { - ret.push_back(item); - } - } - return ret; -} - -S32 LLScrollListCtrl::getNumSelected() const -{ - S32 numSelected = 0; - - for(item_list::const_iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter) - { - LLScrollListItem* item = *iter; - if (item->getSelected()) - { - ++numSelected; - } - } - - return numSelected; -} - -S32 LLScrollListCtrl::getFirstSelectedIndex() const -{ - S32 CurSelectedIndex = 0; - - // make sure sort is up to date before returning an index - updateSort(); - - item_list::const_iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - if (item->getSelected()) - { - return CurSelectedIndex; - } - CurSelectedIndex++; - } - - return -1; -} - -LLScrollListItem* LLScrollListCtrl::getFirstData() const -{ - if (mItemList.size() == 0) - { - return NULL; - } - return mItemList[0]; -} - -LLScrollListItem* LLScrollListCtrl::getLastData() const -{ - if (mItemList.size() == 0) - { - return NULL; - } - return mItemList[mItemList.size() - 1]; -} - -std::vector LLScrollListCtrl::getAllData() const -{ - std::vector ret; - item_list::const_iterator iter; - for(iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - ret.push_back(item); - } - return ret; -} - -// returns first matching item -LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const -{ - std::string string_val = sd.asString(); - - item_list::const_iterator iter; - for(iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - // assumes string representation is good enough for comparison - if (item->getValue().asString() == string_val) - { - return item; - } - } - return NULL; -} - - -void LLScrollListCtrl::reshape( S32 width, S32 height, bool called_from_parent ) -{ - LLUICtrl::reshape( width, height, called_from_parent ); - - updateLayout(); -} - -void LLScrollListCtrl::updateLayout() -{ - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - // reserve room for column headers, if needed - S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); - mItemListRect.setOriginAndSize( - mBorderThickness, - mBorderThickness, - getRect().getWidth() - 2 * mBorderThickness, - getRect().getHeight() - (2 * mBorderThickness ) - heading_size ); - - if (mCommentTextView == NULL) - { - mCommentTextView = getChildView("comment_text"); - } - - mCommentTextView->setShape(mItemListRect); - - // how many lines of content in a single "page" - S32 page_lines = getLinesPerPage(); - - bool scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight(); - if (scrollbar_visible) - { - // provide space on the right for scrollbar - mItemListRect.mRight = getRect().getWidth() - mBorderThickness - scrollbar_size; - } - - mScrollbar->setOrigin(getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom); - mScrollbar->reshape(scrollbar_size, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); - mScrollbar->setPageSize(page_lines); - mScrollbar->setDocSize( getItemCount() ); - mScrollbar->setVisible(scrollbar_visible); - - dirtyColumns(); -} - -// Attempt to size the control to show all items. -// Do not make larger than width or height. -void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height) -{ - S32 height = llmin( getRequiredRect().getHeight(), max_height ); - if(mPageLines) - height = llmin( mPageLines * mLineHeight + 2*mBorderThickness + (mDisplayColumnHeaders ? mHeadingHeight : 0), height ); - - S32 width = getRect().getWidth(); - - reshape( width, height ); -} - - -LLRect LLScrollListCtrl::getRequiredRect() -{ - S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); - S32 height = (mLineHeight * getItemCount()) - + (2 * mBorderThickness ) - + heading_size; - S32 width = getRect().getWidth(); - - return LLRect(0, height, width, 0); -} - - -bool LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, bool requires_column ) -{ - bool not_too_big = getItemCount() < mMaxItemCount; - if (not_too_big) - { - switch( pos ) - { - case ADD_TOP: - mItemList.push_front(item); - setNeedsSort(); - break; - - case ADD_DEFAULT: - case ADD_BOTTOM: - mItemList.push_back(item); - setNeedsSort(); - break; - - default: - llassert(0); - mItemList.push_back(item); - setNeedsSort(); - break; - } - - // create new column on demand - if (mColumns.empty() && requires_column) - { - LLScrollListColumn::Params col_params; - col_params.name = "default_column"; - col_params.header.label = ""; - col_params.width.dynamic_width = true; - addColumn(col_params); - } - - S32 num_cols = item->getNumColumns(); - S32 i = 0; - for (LLScrollListCell* cell = item->getColumn(i); i < num_cols; cell = item->getColumn(++i)) - { - if (i >= (S32)mColumnsIndexed.size()) break; - - cell->setWidth(mColumnsIndexed[i]->getWidth()); - } - - updateLineHeightInsert(item); - - updateLayout(); - } - - return not_too_big; -} - -// NOTE: This is *very* expensive for large lists, especially when we are dirtying the list every frame -// while receiving a long list of names. -// *TODO: Use bookkeeping to make this an incramental cost with item additions -S32 LLScrollListCtrl::calcMaxContentWidth() -{ - const S32 HEADING_TEXT_PADDING = 25; - const S32 COLUMN_TEXT_PADDING = 10; - - S32 max_item_width = 0; - - ordered_columns_t::iterator column_itor; - for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) - { - LLScrollListColumn* column = *column_itor; - if (!column) continue; - - if (mColumnWidthsDirty) - { - // update max content width for this column, by looking at all items - column->mMaxContentWidth = column->mHeader ? LLFontGL::getFontSansSerifSmall()->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); - if (!cellp) continue; - - column->mMaxContentWidth = llmax(LLFontGL::getFontSansSerifSmall()->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); - } - } - max_item_width += column->mMaxContentWidth; - } - mColumnWidthsDirty = false; - - return max_item_width; -} - -bool LLScrollListCtrl::updateColumnWidths() -{ - bool width_changed = false; - ordered_columns_t::iterator column_itor; - for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) - { - LLScrollListColumn* column = *column_itor; - if (!column) continue; - - // update column width - S32 new_width = 0; - if (column->mRelWidth >= 0) - { - new_width = (S32)ll_round(column->mRelWidth*mItemListRect.getWidth()); - } - else if (column->mDynamicWidth) - { - new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns; - } - else - { - new_width = column->getWidth(); - } - - if (column->getWidth() != new_width) - { - column->setWidth(new_width); - width_changed = true; - } - } - return width_changed; -} - -// Line height is the max height of all the cells in all the items. -void LLScrollListCtrl::updateLineHeight() -{ - mLineHeight = 0; - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - S32 num_cols = itemp->getNumColumns(); - S32 i = 0; - for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) - { - mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding ); - } - } -} - -// when the only change to line height is from an insert, we needn't scan the entire list -void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp) -{ - S32 num_cols = itemp->getNumColumns(); - S32 i = 0; - for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) - { - mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding ); - } -} - - -void LLScrollListCtrl::updateColumns(bool force_update) -{ - if (!mColumnsDirty && !force_update) - return; - - mColumnsDirty = false; - - bool columns_changed_width = updateColumnWidths(); - - // update column headers - std::vector::iterator column_ordered_it; - S32 left = mItemListRect.mLeft; - LLScrollColumnHeader* last_header = NULL; - for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it) - { - LLScrollListColumn* column = *column_ordered_it; - if (!column || column->getWidth() < 0) - { - // skip hidden columns - continue; - } - - if (column->mHeader) - { - column->mHeader->updateResizeBars(); - - last_header = column->mHeader; - S32 top = mItemListRect.mTop; - S32 right = left + column->getWidth(); - - if (column->mIndex != (S32)mColumnsIndexed.size()-1) - { - right += mColumnPadding; - } - right = llmax(left, llmin(mItemListRect.getWidth(), right)); - S32 header_width = right - left; - - last_header->reshape(header_width, mHeadingHeight); - last_header->translate( - left - last_header->getRect().mLeft, - top - last_header->getRect().mBottom); - last_header->setVisible(mDisplayColumnHeaders && header_width > 0); - left = right; - } - } - - bool header_changed_width = false; - // expand last column header we encountered to full list width - if (last_header) - { - S32 old_width = last_header->getColumn()->getWidth(); - S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft); - last_header->reshape(new_width, last_header->getRect().getHeight()); - last_header->setVisible(mDisplayColumnHeaders && new_width > 0); - if (old_width != new_width) - { - last_header->getColumn()->setWidth(new_width); - header_changed_width = true; - } - } - - // propagate column widths to individual cells - if (columns_changed_width || force_update) - { - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - S32 num_cols = itemp->getNumColumns(); - S32 i = 0; - for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) - { - if (i >= (S32)mColumnsIndexed.size()) break; - - cell->setWidth(mColumnsIndexed[i]->getWidth()); - } - } - } - else if (header_changed_width) - { - item_list::iterator iter; - S32 index = last_header->getColumn()->mIndex; // Not always identical to last column! - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - LLScrollListCell* cell = itemp->getColumn(index); - if (cell) - { - cell->setWidth(last_header->getColumn()->getWidth()); - } - } - } -} - -void LLScrollListCtrl::setHeadingHeight(S32 heading_height) -{ - mHeadingHeight = heading_height; - - updateLayout(); - -} -void LLScrollListCtrl::setPageLines(S32 new_page_lines) -{ - mPageLines = new_page_lines; - - updateLayout(); -} - -bool LLScrollListCtrl::selectFirstItem() -{ - bool success = false; - - // our $%&@#$()^%#$()*^ iterators don't let us check against the first item inside out iteration - bool first_item = true; - - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - if( first_item && itemp->getEnabled() ) - { - if (!itemp->getSelected()) - { - switch (mSelectionType) - { - case CELL: - selectItem(itemp, 0); - break; - case HEADER: - case ROW: - selectItem(itemp, -1); - } - } - success = true; - mOriginalSelection = 0; - } - else - { - deselectItem(itemp); - } - first_item = false; - } - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - return success; -} - -// Deselects all other items -// virtual -bool LLScrollListCtrl::selectNthItem( S32 target_index ) -{ - return selectItemRange(target_index, target_index); -} - -// virtual -bool LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) -{ - if (mItemList.empty()) - { - return false; - } - - // make sure sort is up to date - updateSort(); - - S32 listlen = (S32)mItemList.size(); - first_index = llclamp(first_index, 0, listlen-1); - - if (last_index < 0) - last_index = listlen-1; - else - last_index = llclamp(last_index, first_index, listlen-1); - - bool success = false; - S32 index = 0; - for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ) - { - LLScrollListItem *itemp = *iter; - if(!itemp) - { - iter = mItemList.erase(iter); - continue ; - } - - if( index >= first_index && index <= last_index ) - { - if( itemp->getEnabled() ) - { - // TODO: support range selection for cells - selectItem(itemp, -1, false); - success = true; - } - } - else - { - deselectItem(itemp); - } - index++; - iter++ ; - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - - mSearchString.clear(); - - return success; -} - - -void LLScrollListCtrl::swapWithNext(S32 index) -{ - if (index >= ((S32)mItemList.size() - 1)) - { - // At end of list, doesn't do anything - return; - } - updateSort(); - LLScrollListItem *cur_itemp = mItemList[index]; - mItemList[index] = mItemList[index + 1]; - mItemList[index + 1] = cur_itemp; -} - - -void LLScrollListCtrl::swapWithPrevious(S32 index) -{ - if (index <= 0) - { - // At beginning of list, don't do anything - } - - updateSort(); - LLScrollListItem *cur_itemp = mItemList[index]; - mItemList[index] = mItemList[index - 1]; - mItemList[index - 1] = cur_itemp; -} - - -void LLScrollListCtrl::deleteSingleItem(S32 target_index) -{ - if (target_index < 0 || target_index >= (S32)mItemList.size()) - { - return; - } - - updateSort(); - - LLScrollListItem *itemp; - itemp = mItemList[target_index]; - if (itemp == mLastSelected) - { - mLastSelected = NULL; - } - delete itemp; - mItemList.erase(mItemList.begin() + target_index); - dirtyColumns(); -} - -//FIXME: refactor item deletion -void LLScrollListCtrl::deleteItems(const LLSD& sd) -{ - item_list::iterator iter; - for (iter = mItemList.begin(); iter < mItemList.end(); ) - { - LLScrollListItem* itemp = *iter; - if (itemp->getValue().asString() == sd.asString()) - { - if (itemp == mLastSelected) - { - mLastSelected = NULL; - } - delete itemp; - iter = mItemList.erase(iter); - } - else - { - iter++; - } - } - - dirtyColumns(); -} - -void LLScrollListCtrl::deleteSelectedItems() -{ - item_list::iterator iter; - for (iter = mItemList.begin(); iter < mItemList.end(); ) - { - LLScrollListItem* itemp = *iter; - if (itemp->getSelected()) - { - delete itemp; - iter = mItemList.erase(iter); - } - else - { - iter++; - } - } - mLastSelected = NULL; - dirtyColumns(); -} - -void LLScrollListCtrl::clearHighlightedItems() -{ - for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter) - { - (*iter)->setHighlighted(false); - } -} - -void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index) -{ - if (mHighlightedItem != target_index) - { - if (mHighlightedItem >= 0 && mHighlightedItem < mItemList.size()) - { - mItemList[mHighlightedItem]->setHoverCell(-1); - } - mHighlightedItem = target_index; - } -} - -S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) -{ - item_list::iterator iter; - S32 count = 0; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - uuid_vec_t::iterator iditr; - for(iditr = ids.begin(); iditr != ids.end(); ++iditr) - { - if (item->getEnabled() && (item->getUUID() == (*iditr))) - { - // TODO: support multiple selection for cells - selectItem(item, -1, false); - ++count; - break; - } - } - if(ids.end() != iditr) ids.erase(iditr); - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - return count; -} - -S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const -{ - updateSort(); - - S32 index = 0; - item_list::const_iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - if (target_item == itemp) - { - return index; - } - index++; - } - return -1; -} - -S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const -{ - updateSort(); - - S32 index = 0; - item_list::const_iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - if (target_id == itemp->getUUID()) - { - return index; - } - index++; - } - return -1; -} - -void LLScrollListCtrl::selectPrevItem( bool extend_selection) -{ - LLScrollListItem* prev_item = NULL; - - if (!getFirstSelected()) - { - // select last item - selectNthItem(getItemCount() - 1); - } - else - { - updateSort(); - - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* cur_item = *iter; - - if (cur_item->getSelected()) - { - if (prev_item) - { - selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection); - } - else - { - reportInvalidInput(); - } - break; - } - - // don't allow navigation to disabled elements - prev_item = cur_item->getEnabled() ? cur_item : prev_item; - } - } - - if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement)) - { - commitIfChanged(); - } - - mSearchString.clear(); -} - - -void LLScrollListCtrl::selectNextItem( bool extend_selection) -{ - LLScrollListItem* next_item = NULL; - - if (!getFirstSelected()) - { - selectFirstItem(); - } - else - { - updateSort(); - - item_list::reverse_iterator iter; - for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++) - { - LLScrollListItem* cur_item = *iter; - - if (cur_item->getSelected()) - { - if (next_item) - { - selectItem(next_item, cur_item->getSelectedCell(), !extend_selection); - } - else - { - reportInvalidInput(); - } - break; - } - - // don't allow navigation to disabled items - next_item = cur_item->getEnabled() ? cur_item : next_item; - } - } - - if (mCommitOnKeyboardMovement) - { - onCommit(); - } - - mSearchString.clear(); -} - - - -void LLScrollListCtrl::deselectAllItems(bool no_commit_on_change) -{ - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - deselectItem(item); - } - - if (mCommitOnSelectionChange && !no_commit_on_change) - { - commitIfChanged(); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Use this to add comment text such as "Searching", which ignores column settings of list - -void LLScrollListCtrl::setCommentText(const std::string& comment_text) -{ - getChild("comment_text")->setValue(comment_text); -} - -LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos) -{ - LLScrollListItem::Params separator_params; - separator_params.enabled(false); - LLScrollListCell::Params column_params; - column_params.type = "icon"; - column_params.value = "menu_separator"; - column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); - column_params.font_halign = LLFontGL::HCENTER; - separator_params.columns.add(column_params); - return addRow( separator_params, pos ); -} - -// Selects first enabled item of the given name. -// Returns false if item not found. -// Calls getItemByLabel in order to combine functionality -bool LLScrollListCtrl::selectItemByLabel(const std::string& label, bool case_sensitive, S32 column/* = 0*/) -{ - deselectAllItems(true); // ensure that no stale items are selected, even if we don't find a match - LLScrollListItem* item = getItemByLabel(label, case_sensitive, column); - - bool found = NULL != item; - if (found) - { - selectItem(item, -1); - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - - return found; -} - -LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, bool case_sensitive, S32 column) -{ - if (label.empty()) //RN: assume no empty items - { - return NULL; - } - - std::string target_text = label; - if (!case_sensitive) - { - LLStringUtil::toLower(target_text); - } - - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - std::string item_text = item->getColumn(column)->getValue().asString(); // Only select enabled items with matching names - if (!case_sensitive) - { - LLStringUtil::toLower(item_text); - } - if(item_text == target_text) - { - return item; - } - } - return NULL; -} - - -bool LLScrollListCtrl::selectItemByPrefix(const std::string& target, bool case_sensitive, S32 column) -{ - return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive, column); -} - -// Selects first enabled item that has a name where the name's first part matched the target string. -// Returns false if item not found. -bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target, bool case_sensitive, S32 column) -{ - bool found = false; - - LLWString target_trimmed( target ); - S32 target_len = target_trimmed.size(); - - if( 0 == target_len ) - { - // Is "" a valid choice? - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - // Only select enabled items with matching names - LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column); - bool select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : false; - if (select) - { - selectItem(item, -1); - found = true; - break; - } - } - } - else - { - if (!case_sensitive) - { - // do comparisons in lower case - LLWStringUtil::toLower(target_trimmed); - } - - for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - - // Only select enabled items with matching names - LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column); - if (!cellp) - { - continue; - } - LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); - if (!case_sensitive) - { - LLWStringUtil::toLower(item_label); - } - // remove extraneous whitespace from searchable label - LLWString trimmed_label = item_label; - LLWStringUtil::trim(trimmed_label); - - bool select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0; - - if (select) - { - // find offset of matching text (might have leading whitespace) - S32 offset = item_label.find(target_trimmed); - cellp->highlightText(offset, target_trimmed.size()); - selectItem(item, -1); - found = true; - break; - } - } - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - - return found; -} - -U32 LLScrollListCtrl::searchItems(const std::string& substring, bool case_sensitive, bool focus) -{ - return searchItems(utf8str_to_wstring(substring), case_sensitive, focus); -} - -U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitive, bool focus) -{ - U32 found = 0; - - LLWString substring_trimmed(substring); - S32 len = substring_trimmed.size(); - - if (0 == len) - { - // at the moment search for empty element is not supported - return 0; - } - else - { - deselectAllItems(true); - if (!case_sensitive) - { - // do comparisons in lower case - LLWStringUtil::toLower(substring_trimmed); - } - - for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - // Only select enabled items with matching names - if (!item->getEnabled()) - { - continue; - } - LLScrollListCell* cellp = item->getColumn(getSearchColumn()); - if (!cellp) - { - continue; - } - LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); - if (!case_sensitive) - { - LLWStringUtil::toLower(item_label); - } - // remove extraneous whitespace from searchable label - LLWStringUtil::trim(item_label); - - size_t found_iter = item_label.find(substring_trimmed); - - if (found_iter != std::string::npos) - { - // find offset of matching text - cellp->highlightText(found_iter, substring_trimmed.size()); - selectItem(item, -1, false); - - found++; - - if (!mAllowMultipleSelection) - { - break; - } - } - } - } - - if (focus && found != 0) - { - mNeedsScroll = true; - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - - return found; -} - -const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const -{ - LLScrollListItem* item; - - item = getFirstSelected(); - if (item) - { - return item->getColumn(column)->getValue().asString(); - } - - return LLStringUtil::null; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which -// has an associated, unique UUID, and only one of which can be selected at a time. - -LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, bool enabled) -{ - if (getItemCount() < mMaxItemCount) - { - LLScrollListItem::Params item_p; - item_p.enabled(enabled); - item_p.value(id); - item_p.columns.add().value(item_text).type("text"); - - return addRow( item_p, pos ); - } - return NULL; -} - -// Select the line or lines that match this UUID -bool LLScrollListCtrl::selectByID( const LLUUID& id ) -{ - return selectByValue( LLSD(id) ); -} - -bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected) -{ - bool found = false; - - if (selected && !mAllowMultipleSelection) deselectAllItems(true); - - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - if (item->getEnabled()) - { - if (value.isBinary()) - { - if (item->getValue().isBinary()) - { - LLSD::Binary data1 = value.asBinary(); - LLSD::Binary data2 = item->getValue().asBinary(); - found = std::equal(data1.begin(), data1.end(), data2.begin()); - } - } - else - { - found = item->getValue().asString() == value.asString(); - } - - if (found) - { - if (selected) - { - selectItem(item, -1); - } - else - { - deselectItem(item); - } - break; - } - } - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - - return found; -} - -bool LLScrollListCtrl::isSelected(const LLSD& value) const -{ - item_list::const_iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - if (item->getValue().asString() == value.asString()) - { - return item->getSelected(); - } - } - return false; -} - -LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() const -{ - LLScrollListItem* item = getFirstSelected(); - - if (item) - { - return item->getUUID(); - } - - return LLUUID::null; -} - -LLSD LLScrollListCtrl::getSelectedValue() -{ - LLScrollListItem* item = getFirstSelected(); - - if (item) - { - return item->getValue(); - } - else - { - return LLSD(); - } -} - -void LLScrollListCtrl::drawItems() -{ - S32 x = mItemListRect.mLeft; - S32 y = mItemListRect.mTop - mLineHeight; - - // allow for partial line at bottom - S32 num_page_lines = getLinesPerPage(); - - LLRect item_rect; - - LLGLSUIDefault gls_ui; - - F32 alpha = getDrawContext().mAlpha; - - { - LLLocalClipRect clip(mItemListRect); - - S32 cur_y = y; - - S32 max_columns = 0; - - LLColor4 highlight_color = LLColor4::white; // ex: text inside cells - static LLUICachedControl type_ahead_timeout ("TypeAheadTimeout", 0); - highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout(), 0.4f, 0.f); - - S32 first_line = mScrollLines; - S32 last_line = llmin((S32)mItemList.size() - 1, mScrollLines + getLinesPerPage()); - - if (first_line >= mItemList.size()) - { - return; - } - item_list::iterator iter; - for (S32 line = first_line; line <= last_line; line++) - { - LLScrollListItem* item = mItemList[line]; - - item_rect.setOriginAndSize( - x, - cur_y, - mItemListRect.getWidth(), - mLineHeight ); - item->setRect(item_rect); - - max_columns = llmax(max_columns, item->getNumColumns()); - - LLColor4 fg_color; - LLColor4 hover_color(LLColor4::transparent); - LLColor4 select_color(LLColor4::transparent); - - if( mScrollLines <= line && line < mScrollLines + num_page_lines ) - { - fg_color = (item->getEnabled() ? mFgUnselectedColor.get() : mFgDisabledColor.get()); - if( item->getSelected() && mCanSelect) - { - if(item->getHighlighted()) // if it's highlighted, average the colors - { - select_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f); - } - else // otherwise just select-highlight it - { - select_color = mBgSelectedColor.get(); - } - - fg_color = (item->getEnabled() ? mFgSelectedColor.get() : mFgDisabledColor.get()); - } - if (mHighlightedItem == line && mCanSelect) - { - if(item->getHighlighted()) // if it's highlighted, average the colors - { - hover_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f); - } - else // otherwise just hover-highlight it - { - hover_color = mHoveredColor.get(); - } - } - else if (item->getHighlighted()) - { - hover_color = mHighlightedColor.get(); - } - else - { - if (mDrawStripes && (line % 2 == 0) && (max_columns > 1)) - { - hover_color = mBgStripeColor.get(); - } - } - - if (!item->getEnabled()) - { - hover_color = mBgReadOnlyColor.get(); - } - - item->draw(item_rect, fg_color % alpha, hover_color% alpha, select_color% alpha, highlight_color % alpha, mColumnPadding); - - cur_y -= mLineHeight; - } - } - } -} - - -void LLScrollListCtrl::draw() -{ - LLLocalClipRect clip(getLocalRect()); - - // if user specifies sort, make sure it is maintained - updateSort(); - - if (mNeedsScroll) - { - scrollToShowSelected(); - mNeedsScroll = false; - } - LLRect background(0, getRect().getHeight(), getRect().getWidth(), 0); - // Draw background - if (mBackgroundVisible) - { - F32 alpha = getCurrentTransparency(); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha ); - } - - updateColumns(); - - getChildView("comment_text")->setVisible(mItemList.empty()); - - drawItems(); - - if (mBorder) - { - mBorder->setKeyboardFocusHighlight(hasFocus()); - } - - LLUICtrl::draw(); -} - -void LLScrollListCtrl::setEnabled(bool enabled) -{ - mCanSelect = enabled; - setTabStop(enabled); - mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize()); -} - -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; -} - -bool LLScrollListCtrl::handleScrollHWheel(S32 x, S32 y, S32 clicks) -{ - bool handled = false; - // Pretend the mouse is over the scrollbar - handled = mScrollbar->handleScrollHWheel( 0, 0, clicks ); - - if (mMouseWheelOpaque) - { - return true; - } - - return handled; -} - -// *NOTE: Requires a valid row_index and column_index -LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index) -{ - LLRect cell_rect; - S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; - S32 rect_bottom = getRowOffsetFromIndex(row_index); - LLScrollListColumn* columnp = getColumn(column_index); - cell_rect.setOriginAndSize(rect_left, rect_bottom, - /*rect_left + */columnp->getWidth(), mLineHeight); - return cell_rect; -} - -bool LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) -{ - S32 column_index = getColumnIndexFromOffset(x); - LLScrollListColumn* columnp = getColumn(column_index); - - if (columnp == NULL) return false; - - bool handled = false; - // show tooltip for full name of hovered item if it has been truncated - LLScrollListItem* hit_item = hitItem(x, y); - if (hit_item) - { - LLScrollListCell* hit_cell = hit_item->getColumn(column_index); - if (!hit_cell) return false; - if (hit_cell - && hit_cell->isText() - && hit_cell->needsToolTip()) - { - S32 row_index = getItemIndex(hit_item); - LLRect cell_rect = getCellRect(row_index, column_index); - // Convert rect local to screen coordinates - LLRect sticky_rect; - localRectToScreen(cell_rect, &sticky_rect); - - // display tooltip exactly over original cell, in same font - LLToolTipMgr::instance().show(LLToolTip::Params() - .message(hit_cell->getToolTip()) - .font(LLFontGL::getFontEmojiSmall()) - .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 6)) - .delay_time(0.2f) - .sticky_rect(sticky_rect)); - } - handled = true; - } - - // otherwise, look for a tooltip associated with this column - LLScrollColumnHeader* headerp = columnp->mHeader; - if (headerp && !handled) - { - handled = headerp->handleToolTip(x, y, mask); - } - - return handled; -} - -bool LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) -{ - if (!mCanSelect) return false; - - bool selection_changed = false; - - LLScrollListItem* hit_item = hitItem(x, y); - - if( hit_item ) - { - if( mAllowMultipleSelection ) - { - if (mask & MASK_SHIFT) - { - if (mLastSelected == NULL) - { - selectItem(hit_item, getColumnIndexFromOffset(x)); - } - else - { - // Select everthing between mLastSelected and hit_item - bool selecting = false; - item_list::iterator itor; - // If we multiselect backwards, we'll stomp on mLastSelected, - // meaning that we never stop selecting until hitting max or - // the end of the list. - LLScrollListItem* lastSelected = mLastSelected; - for (itor = mItemList.begin(); itor != mItemList.end(); ++itor) - { - if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable) - { - if(mOnMaximumSelectCallback) - { - mOnMaximumSelectCallback(); - } - break; - } - LLScrollListItem *item = *itor; - if (item == hit_item || item == lastSelected) - { - selectItem(item, getColumnIndexFromOffset(x), false); - selecting = !selecting; - if (hit_item == lastSelected) - { - // stop selecting now, since we just clicked on our last selected item - selecting = false; - } - } - if (selecting) - { - selectItem(item, getColumnIndexFromOffset(x), false); - } - } - } - } - else if (mask & MASK_CONTROL) - { - if (hit_item->getSelected()) - { - deselectItem(hit_item); - } - else - { - if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) - { - selectItem(hit_item, getColumnIndexFromOffset(x), false); - } - else - { - if(mOnMaximumSelectCallback) - { - mOnMaximumSelectCallback(); - } - } - } - } - else - { - deselectAllItems(true); - selectItem(hit_item, getColumnIndexFromOffset(x)); - } - } - else - { - selectItem(hit_item, getColumnIndexFromOffset(x)); - } - - selection_changed = mSelectionChanged; - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } - - // clear search string on mouse operations - mSearchString.clear(); - } - else - { - //mLastSelected = NULL; - //deselectAllItems(true); - } - - return selection_changed; -} - - -bool LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = childrenHandleMouseDown(x, y, mask) != NULL; - - if( !handled ) - { - // set keyboard focus first, in case click action wants to move focus elsewhere - setFocus(true); - - // clear selection changed flag because user is starting a selection operation - mSelectionChanged = false; - - handleClick(x, y, mask); - } - - return true; -} - -bool LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if (hasMouseCapture()) - { - // release mouse capture immediately so - // scroll to show selected logic will work - gFocusMgr.setMouseCapture(NULL); - if(mask == MASK_NONE) - { - selectItemAt(x, y, mask); - mNeedsScroll = true; - } - } - - // always commit when mouse operation is completed inside list - if (mItemListRect.pointInRect(x,y)) - { - mDirty = mDirty || mSelectionChanged; - mSelectionChanged = false; - onCommit(); - } - - return LLUICtrl::handleMouseUp(x, y, mask); -} - -// virtual -bool LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - LLScrollListItem *item = hitItem(x, y); - if (item) - { - // check to see if we have a UUID for this row - std::string id = item->getValue().asString(); - LLUUID uuid(id); - if (! uuid.isNull() && mContextMenuType != MENU_NONE) - { - // set up the callbacks for all of the avatar/group menu items - // (N.B. callbacks don't take const refs as id is local scope) - bool is_group = (mContextMenuType == MENU_GROUP); - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - registrar.add("Url.ShowProfile", boost::bind(&LLScrollListCtrl::showProfile, id, is_group)); - registrar.add("Url.SendIM", boost::bind(&LLScrollListCtrl::sendIM, id)); - registrar.add("Url.AddFriend", boost::bind(&LLScrollListCtrl::addFriend, id)); - registrar.add("Url.RemoveFriend", boost::bind(&LLScrollListCtrl::removeFriend, id)); - registrar.add("Url.ReportAbuse", boost::bind(&LLScrollListCtrl::reportAbuse, id, is_group)); - registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group)); - registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group)); - registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group)); - - // create the context menu from the XUI file and display it - std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml"; - auto menu = mPopupMenuHandle.get(); - if (menu) - { - menu->die(); - mPopupMenuHandle.markDead(); - } - llassert(LLMenuGL::sMenuContainer != NULL); - menu = LLUICtrlFactory::getInstance()->createFromFile( - menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); - if (menu) - { - mPopupMenuHandle = menu->getHandle(); - if (mIsFriendSignal) - { - bool isFriend = *(*mIsFriendSignal)(uuid); - LLView* addFriendButton = menu->getChild("add_friend"); - LLView* removeFriendButton = menu->getChild("remove_friend"); - - if (addFriendButton && removeFriendButton) - { - addFriendButton->setEnabled(!isFriend); - removeFriendButton->setEnabled(isFriend); - } - } - - menu->show(x, y); - LLMenuGL::showPopup(this, menu, x, y); - return true; - } - } - return LLUICtrl::handleRightMouseDown(x, y, mask); - } - return false; -} - -void LLScrollListCtrl::showProfile(std::string id, bool is_group) -{ - // show the resident's profile or the group profile - std::string sltype = is_group ? "group" : "agent"; - std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; - LLUrlAction::showProfile(slurl); -} - -void LLScrollListCtrl::sendIM(std::string id) -{ - // send im to the resident - std::string slurl = "secondlife:///app/agent/" + id + "/about"; - LLUrlAction::sendIM(slurl); -} - -void LLScrollListCtrl::addFriend(std::string id) -{ - // add resident to friends list - std::string slurl = "secondlife:///app/agent/" + id + "/about"; - LLUrlAction::addFriend(slurl); -} - -void LLScrollListCtrl::removeFriend(std::string id) -{ - std::string slurl = "secondlife:///app/agent/" + id + "/about"; - LLUrlAction::removeFriend(slurl); -} - -void LLScrollListCtrl::reportAbuse(std::string id, bool is_group) -{ - if (!is_group) - { - std::string slurl = "secondlife:///app/agent/" + id + "/about"; - LLUrlAction::reportAbuse(slurl); - } -} - -void LLScrollListCtrl::showNameDetails(std::string id, bool is_group) -{ - // open the resident's details or the group details - std::string sltype = is_group ? "group" : "agent"; - std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; - LLUrlAction::clickAction(slurl, true); -} - -void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group) -{ - // copy the name of the avatar or group to the clipboard - std::string name; - if (is_group) - { - gCacheName->getGroupName(LLUUID(id), name); - } - else - { - LLAvatarName av_name; - LLAvatarNameCache::get(LLUUID(id), &av_name); - name = av_name.getAccountName(); - } - LLUrlAction::copyURLToClipboard(name); -} - -void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group) -{ - // copy a SLURL for the avatar or group to the clipboard - std::string sltype = is_group ? "group" : "agent"; - std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; - LLUrlAction::copyURLToClipboard(slurl); -} - -bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - //bool handled = false; - bool handled = handleClick(x, y, mask); - - if (!handled) - { - // Offer the click to the children, even if we aren't enabled - // so the scroll bars will work. - if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) - { - // Run the callback only if an item is being double-clicked. - if( mCanSelect && hitItem(x, y) && mOnDoubleClickCallback ) - { - mOnDoubleClickCallback(); - } - } - } - - return true; -} - -bool LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) -{ - // which row was clicked on? - LLScrollListItem* hit_item = hitItem(x, y); - if (!hit_item) return false; - - // get appropriate cell from that row - S32 column_index = getColumnIndexFromOffset(x); - LLScrollListCell* hit_cell = hit_item->getColumn(column_index); - if (!hit_cell) return false; - - // if cell handled click directly (i.e. clicked on an embedded checkbox) - if (hit_cell->handleClick()) - { - // if item not currently selected, select it - if (!hit_item->getSelected()) - { - selectItemAt(x, y, mask); - gFocusMgr.setMouseCapture(this); - mNeedsScroll = true; - } - - // propagate state of cell to rest of selected column - { - // propagate value of this cell to other selected items - // and commit the respective widgets - LLSD item_value = hit_cell->getValue(); - for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - if (item->getSelected()) - { - LLScrollListCell* cellp = item->getColumn(column_index); - cellp->setValue(item_value); - cellp->onCommit(); - if (mLastSelected == NULL) - { - break; - } - } - } - //FIXME: find a better way to signal cell changes - onCommit(); - } - // eat click (e.g. do not trigger double click callback) - return true; - } - else - { - // treat this as a normal single item selection - selectItemAt(x, y, mask); - gFocusMgr.setMouseCapture(this); - mNeedsScroll = true; - // do not eat click (allow double click callback) - return false; - } -} - -LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) -{ - // Excludes disabled items. - LLScrollListItem* hit_item = NULL; - - updateSort(); - - LLRect item_rect; - item_rect.setLeftTopAndSize( - mItemListRect.mLeft, - mItemListRect.mTop, - mItemListRect.getWidth(), - mLineHeight ); - - // allow for partial line at bottom - S32 num_page_lines = getLinesPerPage(); - - S32 line = 0; - item_list::iterator iter; - for(iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem* item = *iter; - if( mScrollLines <= line && line < mScrollLines + num_page_lines ) - { - if( item->getEnabled() && item_rect.pointInRect( x, y ) ) - { - hit_item = item; - break; - } - - item_rect.translate(0, -mLineHeight); - } - line++; - } - - return hit_item; -} - -S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x) -{ - // which column did we hit? - S32 left = 0; - S32 right = 0; - S32 width = 0; - S32 column_index = 0; - - ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); - ordered_columns_t::const_iterator end = mColumnsIndexed.end(); - for ( ; iter != end; ++iter) - { - width = (*iter)->getWidth() + mColumnPadding; - right += width; - if (left <= x && x < right ) - { - break; - } - - // set left for next column as right of current column - left = right; - column_index++; - } - - return llclamp(column_index, 0, getNumColumns() - 1); -} - - -S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index) -{ - S32 column_offset = 0; - ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); - ordered_columns_t::const_iterator end = mColumnsIndexed.end(); - for ( ; iter != end; ++iter) - { - if (index-- <= 0) - { - return column_offset; - } - column_offset += (*iter)->getWidth() + mColumnPadding; - } - - // when running off the end, return the rightmost pixel - return mItemListRect.mRight; -} - -S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index) -{ - S32 row_bottom = (mItemListRect.mTop - ((index - mScrollLines + 1) * mLineHeight) ); - return row_bottom; -} - - -bool LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) -{ - bool handled = false; - - if (hasMouseCapture()) - { - if(mask == MASK_NONE) - { - selectItemAt(x, y, mask); - mNeedsScroll = true; - } - } - else - if (mCanSelect) - { - LLScrollListItem* item = hitItem(x, y); - if (item) - { - mouseOverHighlightNthItem(getItemIndex(item)); - switch (mSelectionType) - { - case CELL: - item->setHoverCell(getColumnIndexFromOffset(x)); - break; - case HEADER: - { - S32 cell = getColumnIndexFromOffset(x); - if (cell > 0) - { - item->setHoverCell(cell); - } - else - { - item->setHoverCell(-1); - } - break; - } - case ROW: - break; - } - } - else - { - mouseOverHighlightNthItem(-1); - } - } - - handled = LLUICtrl::handleHover( x, y, mask ); - - return handled; -} - -void LLScrollListCtrl::onMouseLeave(S32 x, S32 y, MASK mask) -{ - // clear mouse highlight - mouseOverHighlightNthItem(-1); -} - -bool LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) -{ - bool handled = false; - - // not called from parent means we have keyboard focus or a child does - if (mCanSelect) - { - if (mask == MASK_NONE) - { - switch(key) - { - case KEY_UP: - if (mAllowKeyboardMovement || hasFocus()) - { - // commit implicit in call - selectPrevItem(false); - mNeedsScroll = true; - handled = true; - } - break; - case KEY_DOWN: - if (mAllowKeyboardMovement || hasFocus()) - { - // commit implicit in call - selectNextItem(false); - mNeedsScroll = true; - handled = true; - } - break; - case KEY_LEFT: - if (mAllowKeyboardMovement || hasFocus()) - { - // TODO: support multi-select - LLScrollListItem *item = getFirstSelected(); - if (item) - { - S32 cell = item->getSelectedCell(); - switch (mSelectionType) - { - case CELL: - if (cell < mColumns.size()) cell++; - break; - case HEADER: - if (cell == -1) cell = 1; - else if (cell > 1 && cell < mColumns.size()) cell++; // skip header - break; - case ROW: - cell = -1; - break; - } - item->setSelectedCell(cell); - handled = true; - } - } - break; - case KEY_RIGHT: - if (mAllowKeyboardMovement || hasFocus()) - { - // TODO: support multi-select - LLScrollListItem *item = getFirstSelected(); - if (item) - { - S32 cell = item->getSelectedCell(); - switch (mSelectionType) - { - case CELL: - if (cell >= 0) cell--; - break; - case HEADER: - if (cell > 1) cell--; - else if (cell == 1) cell = -1; // skip header - break; - case ROW: - cell = -1; - break; - } - item->setSelectedCell(cell); - handled = true; - } - } - break; - case KEY_PAGE_UP: - if (mAllowKeyboardMovement || hasFocus()) - { - selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1)); - mNeedsScroll = true; - if (mCommitOnKeyboardMovement - && !mCommitOnSelectionChange) - { - onCommit(); - } - handled = true; - } - break; - case KEY_PAGE_DOWN: - if (mAllowKeyboardMovement || hasFocus()) - { - selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1)); - mNeedsScroll = true; - if (mCommitOnKeyboardMovement - && !mCommitOnSelectionChange) - { - onCommit(); - } - handled = true; - } - break; - case KEY_HOME: - if (mAllowKeyboardMovement || hasFocus()) - { - selectFirstItem(); - mNeedsScroll = true; - if (mCommitOnKeyboardMovement - && !mCommitOnSelectionChange) - { - onCommit(); - } - handled = true; - } - break; - case KEY_END: - if (mAllowKeyboardMovement || hasFocus()) - { - selectNthItem(getItemCount() - 1); - mNeedsScroll = true; - if (mCommitOnKeyboardMovement - && !mCommitOnSelectionChange) - { - onCommit(); - } - handled = true; - } - break; - case KEY_RETURN: - // JC - Special case: Only claim to have handled it - // if we're the special non-commit-on-move - // type. AND we are visible - if (!mCommitOnKeyboardMovement && mask == MASK_NONE) - { - onCommit(); - mSearchString.clear(); - handled = true; - } - break; - case KEY_BACKSPACE: - mSearchTimer.reset(); - if (mSearchString.size()) - { - mSearchString.erase(mSearchString.size() - 1, 1); - } - if (mSearchString.empty()) - { - if (getFirstSelected()) - { - LLScrollListCell* cellp = getFirstSelected()->getColumn(getSearchColumn()); - if (cellp) - { - cellp->highlightText(0, 0); - } - } - } - else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), false)) - { - mNeedsScroll = true; - // update search string only on successful match - mSearchTimer.reset(); - - if (mCommitOnKeyboardMovement - && !mCommitOnSelectionChange) - { - onCommit(); - } - } - break; - default: - break; - } - } - // TODO: multiple: shift-up, shift-down, shift-home, shift-end, select all - } - - return handled; -} - -bool LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return false; - } - - // perform incremental search based on keyboard input - static LLUICachedControl type_ahead_timeout ("TypeAheadTimeout", 0); - if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout) - { - mSearchString.clear(); - } - - // type ahead search is case insensitive - uni_char = LLStringOps::toLower((llwchar)uni_char); - - if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), false)) - { - // update search string only on successful match - mNeedsScroll = true; - mSearchString += uni_char; - mSearchTimer.reset(); - - if (mCommitOnKeyboardMovement - && !mCommitOnSelectionChange) - { - onCommit(); - } - } - // handle iterating over same starting character - else if (isRepeatedChars(mSearchString + (llwchar)uni_char) && !mItemList.empty()) - { - // start from last selected item, in case we previously had a successful match against - // duplicated characters ('AA' matches 'Aaron') - item_list::iterator start_iter = mItemList.begin(); - S32 first_selected = getFirstSelectedIndex(); - - // if we have a selection (> -1) then point iterator at the selected item - if (first_selected > 0) - { - // point iterator to first selected item - start_iter += first_selected; - } - - // start search at first item after current selection - item_list::iterator iter = start_iter; - ++iter; - if (iter == mItemList.end()) - { - iter = mItemList.begin(); - } - - // loop around once, back to previous selection - while(iter != start_iter) - { - LLScrollListItem* item = *iter; - - LLScrollListCell* cellp = item->getColumn(getSearchColumn()); - if (cellp) - { - // Only select enabled items with matching first characters - LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); - if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) - { - selectItem(item, -1); - mNeedsScroll = true; - cellp->highlightText(0, 1); - mSearchTimer.reset(); - - if (mCommitOnKeyboardMovement - && !mCommitOnSelectionChange) - { - onCommit(); - } - - break; - } - } - - ++iter; - if (iter == mItemList.end()) - { - iter = mItemList.begin(); - } - } - } - - return true; -} - - -void LLScrollListCtrl::reportInvalidInput() -{ - make_ui_sound("UISndBadKeystroke"); -} - -bool LLScrollListCtrl::isRepeatedChars(const LLWString& string) const -{ - if (string.empty()) - { - return false; - } - - llwchar first_char = string[0]; - - for (U32 i = 0; i < string.size(); i++) - { - if (string[i] != first_char) - { - return false; - } - } - - return true; -} - -void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, bool select_single_item) -{ - if (!itemp) return; - - if (!itemp->getSelected()) - { - if (mLastSelected) - { - LLScrollListCell* cellp = mLastSelected->getColumn(getSearchColumn()); - if (cellp) - { - cellp->highlightText(0, 0); - } - } - if (select_single_item) - { - deselectAllItems(true); - } - itemp->setSelected(true); - switch (mSelectionType) - { - case CELL: - itemp->setSelectedCell(cell); - break; - case HEADER: - itemp->setSelectedCell(cell <= 0 ? -1 : cell); - break; - case ROW: - itemp->setSelectedCell(-1); - break; - } - mLastSelected = itemp; - mSelectionChanged = true; - } -} - -void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) -{ - if (!itemp) return; - - if (itemp->getSelected()) - { - if (mLastSelected == itemp) - { - mLastSelected = NULL; - } - - itemp->setSelected(false); - LLScrollListCell* cellp = itemp->getColumn(getSearchColumn()); - if (cellp) - { - cellp->highlightText(0, 0); - } - mSelectionChanged = true; - } -} - -void LLScrollListCtrl::commitIfChanged() -{ - if (mSelectionChanged) - { - mDirty = true; - mSelectionChanged = false; - onCommit(); - } -} - -struct SameSortColumn -{ - SameSortColumn(S32 column) : mColumn(column) {} - S32 mColumn; - - bool operator()(std::pair sort_column) { return sort_column.first == mColumn; } -}; - -bool LLScrollListCtrl::setSort(S32 column_idx, bool ascending) -{ - LLScrollListColumn* sort_column = getColumn(column_idx); - if (!sort_column) return false; - - sort_column->mSortDirection = ascending ? LLScrollListColumn::ASCENDING : LLScrollListColumn::DESCENDING; - - sort_column_t new_sort_column(column_idx, ascending); - - setNeedsSort(); - - if (mSortColumns.empty()) - { - mSortColumns.push_back(new_sort_column); - return true; - } - else - { - // grab current sort column - sort_column_t cur_sort_column = mSortColumns.back(); - - // remove any existing sort criterion referencing this column - // and add the new one - mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column_idx)), mSortColumns.end()); - mSortColumns.push_back(new_sort_column); - - // did the sort criteria change? - return (cur_sort_column != new_sort_column); - } -} - -S32 LLScrollListCtrl::getLinesPerPage() -{ - //if mPageLines is NOT provided display all item - if (mPageLines) - { - return mPageLines; - } - else - { - return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount(); - } -} - - -// Called by scrollbar -void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar ) -{ - mScrollLines = new_pos; -} - - -void LLScrollListCtrl::sortByColumn(const std::string& name, bool ascending) -{ - column_map_t::iterator itor = mColumns.find(name); - if (itor != mColumns.end()) - { - sortByColumnIndex((*itor).second->mIndex, ascending); - } -} - -// First column is column 0 -void LLScrollListCtrl::sortByColumnIndex(U32 column, bool ascending) -{ - setSort(column, ascending); - updateSort(); -} - -void LLScrollListCtrl::updateSort() const -{ - if (hasSortOrder() && !isSorted()) - { - // do stable sort to preserve any previous sorts - std::stable_sort( - mItemList.begin(), - mItemList.end(), - SortScrollListItem(mSortColumns,mSortCallback, mAlternateSort)); - - mSorted = true; - } -} - -// for one-shot sorts, does not save sort column/order -void LLScrollListCtrl::sortOnce(S32 column, bool ascending) -{ - std::vector > sort_column; - sort_column.push_back(std::make_pair(column, ascending)); - - // do stable sort to preserve any previous sorts - std::stable_sort( - mItemList.begin(), - mItemList.end(), - SortScrollListItem(sort_column,mSortCallback,mAlternateSort)); -} - -void LLScrollListCtrl::dirtyColumns() -{ - mColumnsDirty = true; - mColumnWidthsDirty = true; - - // need to keep mColumnsIndexed up to date - // just in case someone indexes into it immediately - mColumnsIndexed.resize(mColumns.size()); - - column_map_t::iterator column_itor; - for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) - { - LLScrollListColumn *column = column_itor->second; - mColumnsIndexed[column_itor->second->mIndex] = column; - } -} - - -S32 LLScrollListCtrl::getScrollPos() const -{ - return mScrollbar->getDocPos(); -} - - -void LLScrollListCtrl::setScrollPos( S32 pos ) -{ - mScrollbar->setDocPos( pos ); - - onScrollChange(mScrollbar->getDocPos(), mScrollbar); -} - - -void LLScrollListCtrl::scrollToShowSelected() -{ - // don't scroll automatically when capturing mouse input - // as that will change what is currently under the mouse cursor - if (hasMouseCapture()) - { - return; - } - - updateSort(); - - S32 index = getFirstSelectedIndex(); - if (index < 0) - { - return; - } - - LLScrollListItem* item = mItemList[index]; - if (!item) - { - // I don't THINK this should ever happen. - return; - } - - S32 lowest = mScrollLines; - S32 page_lines = getLinesPerPage(); - S32 highest = mScrollLines + page_lines; - - if (index < lowest) - { - // need to scroll to show item - setScrollPos(index); - } - else if (highest <= index) - { - setScrollPos(index - page_lines + 1); - } -} - -void LLScrollListCtrl::updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width) -{ - mTotalStaticColumnWidth += llmax(0, new_width) - llmax(0, col->getWidth()); -} - -// LLEditMenuHandler functions - -// virtual -void LLScrollListCtrl::copy() -{ - std::string buffer; - - std::vector items = getAllSelected(); - std::vector::iterator itor; - for (itor = items.begin(); itor != items.end(); ++itor) - { - buffer += (*itor)->getContentsCSV() + "\n"; - } - LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length()); -} - -// virtual -bool LLScrollListCtrl::canCopy() const -{ - return (getFirstSelected() != NULL); -} - -// virtual -void LLScrollListCtrl::cut() -{ - copy(); - doDelete(); -} - -// virtual -bool LLScrollListCtrl::canCut() const -{ - return canCopy() && canDoDelete(); -} - -// virtual -void LLScrollListCtrl::selectAll() -{ - // Deselects all other items - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - if( itemp->getEnabled() ) - { - selectItem(itemp, -1, false); - } - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); - } -} - -// virtual -bool LLScrollListCtrl::canSelectAll() const -{ - return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable); -} - -// virtual -void LLScrollListCtrl::deselect() -{ - deselectAllItems(); -} - -// virtual -bool LLScrollListCtrl::canDeselect() const -{ - return getCanSelect(); -} - -void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) -{ - LLScrollListColumn::Params p; - LLParamSDParser parser; - parser.readSD(column, p); - addColumn(p, pos); -} - -void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params, EAddPosition pos) -{ - if (!column_params.validateBlock()) return; - - std::string name = column_params.name; - // if no column name provided, just use ordinal as name - if (name.empty()) - { - name = llformat("%d", mColumnsIndexed.size()); - } - - if (mColumns.find(name) == mColumns.end()) - { - // Add column - mColumns[name] = new LLScrollListColumn(column_params, this); - LLScrollListColumn* new_column = mColumns[name]; - new_column->mIndex = mColumns.size()-1; - - // Add button - if (new_column->getWidth() > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth) - { - if (getNumColumns() > 0) - { - mTotalColumnPadding += mColumnPadding; - } - if (new_column->mRelWidth >= 0) - { - new_column->setWidth((S32)ll_round(new_column->mRelWidth*mItemListRect.getWidth())); - } - else if(new_column->mDynamicWidth) - { - mNumDynamicWidthColumns++; - new_column->setWidth((mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns); - } - S32 top = mItemListRect.mTop; - - S32 left = mItemListRect.mLeft; - for (column_map_t::iterator itor = mColumns.begin(); - itor != mColumns.end(); - ++itor) - { - if (itor->second->mIndex < new_column->mIndex && - itor->second->getWidth() > 0) - { - left += itor->second->getWidth() + mColumnPadding; - } - } - - S32 right = left+new_column->getWidth(); - if (new_column->mIndex != (S32)mColumns.size()-1) - { - right += mColumnPadding; - } - - LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); - - LLScrollColumnHeader::Params params(LLUICtrlFactory::getDefaultParams()); - params.name = "btn_" + name; - params.rect = temp_rect; - params.column = new_column; - params.tool_tip = column_params.tool_tip; - params.tab_stop = false; - params.visible = mDisplayColumnHeaders; - - if(column_params.header.image.isProvided()) - { - params.image_selected = column_params.header.image; - params.image_unselected = column_params.header.image; - } - else - { - params.label = column_params.header.label; - } - - new_column->mHeader = LLUICtrlFactory::create(params); - addChild(new_column->mHeader); - - sendChildToFront(mScrollbar); - } - } - - dirtyColumns(); -} - -// static -void LLScrollListCtrl::onClickColumn(void *userdata) -{ - LLScrollListColumn *info = (LLScrollListColumn*)userdata; - if (!info) return; - - LLScrollListCtrl *parent = info->mParentCtrl; - if (!parent) return; - - if (!parent->mCanSort) return; - - S32 column_index = info->mIndex; - - LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; - bool ascending = column->mSortDirection == LLScrollListColumn::ASCENDING; - if (column->mSortingColumn != column->mName - && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) - { - LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn]; - column_index = info_redir->mIndex; - } - - // if this column is the primary sort key, reverse the direction - if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index) - { - ascending = !parent->mSortColumns.back().second; - } - - parent->sortByColumnIndex(column_index, ascending); - - if (parent->mOnSortChangedCallback) - { - parent->mOnSortChangedCallback(); - } -} - -std::string LLScrollListCtrl::getSortColumnName() -{ - LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first]; - - if (column) return column->mName; - else return ""; -} - -bool LLScrollListCtrl::hasSortOrder() const -{ - return !mSortColumns.empty(); -} - -void LLScrollListCtrl::clearSortOrder() -{ - mSortColumns.clear(); -} - -void LLScrollListCtrl::clearColumns() -{ - column_map_t::iterator itor; - for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) - { - LLScrollColumnHeader *header = itor->second->mHeader; - if (header) - { - removeChild(header); - delete header; - } - } - std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer()); - mColumns.clear(); - mSortColumns.clear(); - mTotalStaticColumnWidth = 0; - mTotalColumnPadding = 0; - - dirtyColumns(); // Clears mColumnsIndexed -} - -void LLScrollListCtrl::setColumnLabel(const std::string& column, const std::string& label) -{ - LLScrollListColumn* columnp = getColumn(column); - if (columnp) - { - columnp->mLabel = label; - if (columnp->mHeader) - { - columnp->mHeader->setLabel(label); - } - } -} - -LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index) -{ - if (index < 0 || index >= (S32)mColumnsIndexed.size()) - { - return NULL; - } - return mColumnsIndexed[index]; -} - -LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name) -{ - column_map_t::iterator column_itor = mColumns.find(name); - if (column_itor != mColumns.end()) - { - return column_itor->second; - } - return NULL; -} - -LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - LLScrollListItem::Params item_params; - LLParamSDParser parser; - parser.readSD(element, item_params); - item_params.userdata = userdata; - return addRow(item_params, pos); -} - -LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_p, EAddPosition pos) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - LLScrollListItem *new_item = new LLScrollListItem(item_p); - return addRow(new_item, item_p, pos); -} - -LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& item_p, EAddPosition pos) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - if (!item_p.validateBlock() || !new_item) return NULL; - new_item->setNumColumns(mColumns.size()); - - // Add any columns we don't already have - S32 col_index = 0; - - for(LLInitParam::ParamIterator::const_iterator itor = item_p.columns.begin(); - itor != item_p.columns.end(); - ++itor) - { - LLScrollListCell::Params cell_p = *itor; - std::string column = cell_p.column; - - // empty columns strings index by ordinal - if (column.empty()) - { - column = llformat("%d", col_index); - } - - LLScrollListColumn* columnp = getColumn(column); - - // create new column on demand - if (!columnp) - { - LLScrollListColumn::Params new_column; - new_column.name = column; - new_column.header.label = column; - - // if width supplied for column, use it, otherwise - // use adaptive width - if (cell_p.width.isProvided()) - { - new_column.width.pixel_width = cell_p.width; - } - addColumn(new_column); - columnp = mColumns[column]; - new_item->setNumColumns(mColumns.size()); - } - - S32 index = columnp->mIndex; - if (!cell_p.width.isProvided()) - { - cell_p.width = columnp->getWidth(); - } - - LLScrollListCell* cell = LLScrollListCell::create(cell_p); - - if (cell) - { - new_item->setColumn(index, cell); - if (columnp->mHeader - && cell->isText() - && !cell->getValue().asString().empty()) - { - columnp->mHeader->setHasResizableElement(true); - } - } - - col_index++; - } - - if (item_p.columns.empty()) - { - if (mColumns.empty()) - { - LLScrollListColumn::Params new_column; - new_column.name = "0"; - - addColumn(new_column); - new_item->setNumColumns(mColumns.size()); - } - - LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value)); - if (cell) - { - LLScrollListColumn* columnp = mColumns.begin()->second; - - new_item->setColumn(0, cell); - if (columnp->mHeader - && cell->isText() - && !cell->getValue().asString().empty()) - { - columnp->mHeader->setHasResizableElement(true); - } - } - } - - // add dummy cells for missing columns - for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it) - { - S32 column_idx = column_it->second->mIndex; - if (new_item->getColumn(column_idx) == NULL) - { - LLScrollListColumn* column_ptr = column_it->second; - LLScrollListCell::Params cell_p; - cell_p.width = column_ptr->getWidth(); - - new_item->setColumn(column_idx, new LLScrollListSpacer(cell_p)); - } - } - - addItem(new_item, pos); - return new_item; -} - -LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) -{ - LLSD entry_id = id; - - if (id.isUndefined()) - { - entry_id = value; - } - - LLScrollListItem::Params item_params; - item_params.value(entry_id); - item_params.columns.add() - .value(value) - .font(LLFontGL::getFontEmojiSmall()); - - return addRow(item_params, pos); -} - -void LLScrollListCtrl::setValue(const LLSD& value ) -{ - LLSD::array_const_iterator itor; - for (itor = value.beginArray(); itor != value.endArray(); ++itor) - { - addElement(*itor); - } -} - -LLSD LLScrollListCtrl::getValue() const -{ - LLScrollListItem *item = getFirstSelected(); - if (!item) return LLSD(); - return item->getValue(); -} - -bool LLScrollListCtrl::operateOnSelection(EOperation op) -{ - if (op == OP_DELETE) - { - deleteSelectedItems(); - return true; - } - else if (op == OP_DESELECT) - { - deselectAllItems(); - } - return false; -} - -bool LLScrollListCtrl::operateOnAll(EOperation op) -{ - if (op == OP_DELETE) - { - clearRows(); - return true; - } - else if (op == OP_DESELECT) - { - deselectAllItems(); - } - else if (op == OP_SELECT) - { - selectAll(); - } - return false; -} -//virtual -void LLScrollListCtrl::setFocus(bool b) -{ - // for tabbing into pristine scroll lists (Finder) - if (!getFirstSelected()) - { - selectFirstItem(); - //onCommit(); // SJB: selectFirstItem() will call onCommit() if appropriate - } - LLUICtrl::setFocus(b); -} - - -// virtual -bool LLScrollListCtrl::isDirty() const -{ - bool grubby = mDirty; - if ( !mAllowMultipleSelection ) - { - grubby = (mOriginalSelection != getFirstSelectedIndex()); - } - return grubby; -} - -// Clear dirty state -void LLScrollListCtrl::resetDirty() -{ - mDirty = false; - mOriginalSelection = getFirstSelectedIndex(); -} - - -//virtual -void LLScrollListCtrl::onFocusReceived() -{ - // forget latent selection changes when getting focus - mSelectionChanged = false; - LLUICtrl::onFocusReceived(); -} - -//virtual -void LLScrollListCtrl::onFocusLost() -{ - if (hasMouseCapture()) - { - gFocusMgr.setMouseCapture(NULL); - } - - mSearchString.clear(); - - LLUICtrl::onFocusLost(); -} - -boost::signals2::connection LLScrollListCtrl::setIsFriendCallback(const is_friend_signal_t::slot_type& cb) -{ - if (!mIsFriendSignal) - { - mIsFriendSignal = new is_friend_signal_t(); - } - return mIsFriendSignal->connect(cb); -} - -bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str) -{ - if (filter_str == "" || filter_str == " ") - { - clearHighlightedItems(); - return false; - } - - bool res = false; - - setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red)); - - std::string filter_str_lc(filter_str); - LLStringUtil::toLower(filter_str_lc); - - std::vector data = getAllData(); - std::vector::iterator iter = data.begin(); - while (iter != data.end()) - { - LLScrollListCell* cell = (*iter)->getColumn(0); - if (cell) - { - std::string value = cell->getValue().asString(); - LLStringUtil::toLower(value); - if (value.find(filter_str_lc) == std::string::npos) - { - (*iter)->setHighlighted(false); - } - else - { - (*iter)->setHighlighted(true); - res = true; - } - } - iter++; - } - return res; -} + /** + * @file llscrolllistctrl.cpp + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). + * + * $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 "linden_common.h" + +#include "llscrolllistctrl.h" + +#include + +#include "llstl.h" +#include "llboost.h" +//#include "indra_constants.h" + +#include "llavatarnamecache.h" +#include "llcheckboxctrl.h" +#include "llclipboard.h" +#include "llfocusmgr.h" +#include "llgl.h" // LLGLSUIDefault() +#include "lllocalcliprect.h" +//#include "llrender.h" +#include "llresmgr.h" +#include "llscrollbar.h" +#include "llscrolllistcell.h" +#include "llstring.h" +#include "llui.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llcontrol.h" +#include "llkeyboard.h" +#include "llviewborder.h" +#include "lltextbox.h" +#include "llsdparam.h" +#include "llcachename.h" +#include "llmenugl.h" +#include "llurlaction.h" +#include "lltooltip.h" + +#include + +static LLDefaultChildRegistry::Register r("scroll_list"); + +// local structures & classes. +struct SortScrollListItem +{ + SortScrollListItem(const std::vector >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal, bool alternate_sort) + : mSortOrders(sort_orders) + , mSortSignal(sort_signal) + , mAltSort(alternate_sort) + {} + + bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) + { + // sort over all columns in order specified by mSortOrders + S32 sort_result = 0; + for (sort_order_t::const_reverse_iterator it = mSortOrders.rbegin(); + it != mSortOrders.rend(); ++it) + { + S32 col_idx = it->first; + bool sort_ascending = it->second; + + S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column? + + const LLScrollListCell *cell1 = i1->getColumn(col_idx); + const LLScrollListCell *cell2 = i2->getColumn(col_idx); + if (cell1 && cell2) + { + if(mSortSignal) + { + sort_result = order * (*mSortSignal)(col_idx,i1, i2); + } + else + { + if (mAltSort && !cell1->getAltValue().asString().empty() && !cell2->getAltValue().asString().empty()) + { + sort_result = order * LLStringUtil::compareDict(cell1->getAltValue().asString(), cell2->getAltValue().asString()); + } + else + { + sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString()); + } + } + if (sort_result != 0) + { + break; // we have a sort order! + } + } + } + + return sort_result < 0; + } + + + typedef std::vector > sort_order_t; + const LLScrollListCtrl::sort_signal_t* mSortSignal; + const sort_order_t& mSortOrders; + const bool mAltSort; +}; + +//--------------------------------------------------------------------------- +// LLScrollListCtrl +//--------------------------------------------------------------------------- + +void LLScrollListCtrl::SelectionTypeNames::declareValues() +{ + declare("row", LLScrollListCtrl::ROW); + declare("cell", LLScrollListCtrl::CELL); + declare("header", LLScrollListCtrl::HEADER); +} + +LLScrollListCtrl::Contents::Contents() +: columns("column"), + rows("row") +{ + addSynonym(columns, "columns"); + addSynonym(rows, "rows"); +} + +LLScrollListCtrl::Params::Params() +: multi_select("multi_select", false), + has_border("draw_border"), + draw_heading("draw_heading"), + search_column("search_column", 0), + selection_type("selection_type", ROW), + sort_column("sort_column", -1), + sort_ascending("sort_ascending", true), + can_sort("can_sort", true), + mouse_wheel_opaque("mouse_wheel_opaque", false), + commit_on_keyboard_movement("commit_on_keyboard_movement", true), + commit_on_selection_change("commit_on_selection_change", false), + heading_height("heading_height"), + page_lines("page_lines", 0), + background_visible("background_visible"), + draw_stripes("draw_stripes"), + column_padding("column_padding"), + row_padding("row_padding", 2), + fg_unselected_color("fg_unselected_color"), + fg_selected_color("fg_selected_color"), + bg_selected_color("bg_selected_color"), + fg_disable_color("fg_disable_color"), + bg_writeable_color("bg_writeable_color"), + bg_readonly_color("bg_readonly_color"), + bg_stripe_color("bg_stripe_color"), + hovered_color("hovered_color"), + highlighted_color("highlighted_color"), + contents(""), + scroll_bar_bg_visible("scroll_bar_bg_visible"), + scroll_bar_bg_color("scroll_bar_bg_color"), + border("border") +{} + +LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) +: LLUICtrl(p), + mLineHeight(0), + mScrollLines(0), + mMouseWheelOpaque(p.mouse_wheel_opaque), + mPageLines(p.page_lines), + mMaxSelectable(0), + mAllowKeyboardMovement(true), + mCommitOnKeyboardMovement(p.commit_on_keyboard_movement), + mCommitOnSelectionChange(p.commit_on_selection_change), + mSelectionChanged(false), + mSelectionType(p.selection_type), + mNeedsScroll(false), + mCanSelect(true), + mCanSort(p.can_sort), + mColumnsDirty(false), + mMaxItemCount(INT_MAX), + mBorderThickness( 2 ), + mOnDoubleClickCallback( NULL ), + mOnMaximumSelectCallback( NULL ), + mOnSortChangedCallback( NULL ), + mHighlightedItem(-1), + mBorder(NULL), + mSortCallback(NULL), + mCommentTextView(NULL), + mNumDynamicWidthColumns(0), + mTotalStaticColumnWidth(0), + mTotalColumnPadding(0), + mSorted(false), + mDirty(false), + mOriginalSelection(-1), + mLastSelected(NULL), + mHeadingHeight(p.heading_height), + mAllowMultipleSelection(p.multi_select), + mDisplayColumnHeaders(p.draw_heading), + mBackgroundVisible(p.background_visible), + mDrawStripes(p.draw_stripes), + mBgWriteableColor(p.bg_writeable_color()), + mBgReadOnlyColor(p.bg_readonly_color()), + mBgSelectedColor(p.bg_selected_color()), + mBgStripeColor(p.bg_stripe_color()), + mFgSelectedColor(p.fg_selected_color()), + mFgUnselectedColor(p.fg_unselected_color()), + mFgDisabledColor(p.fg_disable_color()), + mHighlightedColor(p.highlighted_color()), + mHoveredColor(p.hovered_color()), + mSearchColumn(p.search_column), + mColumnPadding(p.column_padding), + mRowPadding(p.row_padding), + mAlternateSort(false), + mContextMenuType(MENU_NONE), + mIsFriendSignal(NULL) +{ + mItemListRect.setOriginAndSize( + mBorderThickness, + mBorderThickness, + getRect().getWidth() - 2 * mBorderThickness, + getRect().getHeight() - 2 * mBorderThickness ); + + updateLineHeight(); + + // Init the scrollbar + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + LLRect scroll_rect; + scroll_rect.setOriginAndSize( + getRect().getWidth() - mBorderThickness - scrollbar_size, + mItemListRect.mBottom, + scrollbar_size, + mItemListRect.getHeight()); + + LLScrollbar::Params sbparams; + sbparams.name("Scrollbar"); + sbparams.rect(scroll_rect); + sbparams.orientation(LLScrollbar::VERTICAL); + sbparams.doc_size(getItemCount()); + sbparams.doc_pos(mScrollLines); + sbparams.page_size( getLinesPerPage() ); + sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2)); + sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + sbparams.visible(false); + sbparams.bg_visible(p.scroll_bar_bg_visible); + sbparams.bg_color(p.scroll_bar_bg_color); + mScrollbar = LLUICtrlFactory::create (sbparams); + addChild(mScrollbar); + + // Border + if (p.has_border) + { + LLRect border_rect = getLocalRect(); + LLViewBorder::Params params = p.border; + params.rect(border_rect); + mBorder = LLUICtrlFactory::create (params); + addChild(mBorder); + } + + // set border *after* rect is fully initialized + if (mBorder) + { + mBorder->setRect(getLocalRect()); + mBorder->reshape(getRect().getWidth(), getRect().getHeight()); + } + + if (p.sort_column >= 0) + { + sortByColumnIndex(p.sort_column, p.sort_ascending); + } + + + for (LLInitParam::ParamIterator::const_iterator row_it = p.contents.columns.begin(); + row_it != p.contents.columns.end(); + ++row_it) + { + addColumn(*row_it); + } + + for (LLInitParam::ParamIterator::const_iterator row_it = p.contents.rows.begin(); + row_it != p.contents.rows.end(); + ++row_it) + { + addRow(*row_it); + } + + LLTextBox::Params text_p; + text_p.name("comment_text"); + text_p.border_visible(false); + text_p.rect(mItemListRect); + text_p.follows.flags(FOLLOWS_ALL); + // word wrap was added accroding to the EXT-6841 + text_p.wrap(true); + addChild(LLUICtrlFactory::create(text_p)); +} + +S32 LLScrollListCtrl::getSearchColumn() +{ + // search for proper search column + if (mSearchColumn < 0) + { + LLScrollListItem* itemp = getFirstData(); + if (itemp) + { + for(S32 column = 0; column < getNumColumns(); column++) + { + LLScrollListCell* cell = itemp->getColumn(column); + if (cell && cell->isText()) + { + mSearchColumn = column; + break; + } + } + } + } + return llclamp(mSearchColumn, 0, getNumColumns()); +} +/*virtual*/ +bool LLScrollListCtrl::preProcessChildNode(LLXMLNodePtr child) +{ + if (child->hasName("column") || child->hasName("row")) + { + return true; // skip + } + else + { + return false; + } +} + +LLScrollListCtrl::~LLScrollListCtrl() +{ + delete mSortCallback; + + std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); + mItemList.clear(); + clearColumns(); //clears columns and deletes headers + delete mIsFriendSignal; + + auto menu = mPopupMenuHandle.get(); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } +} + + +bool LLScrollListCtrl::setMaxItemCount(S32 max_count) +{ + if (max_count >= getItemCount()) + { + mMaxItemCount = max_count; + } + return (max_count == mMaxItemCount); +} + +S32 LLScrollListCtrl::isEmpty() const +{ + return mItemList.empty(); +} + +S32 LLScrollListCtrl::getItemCount() const +{ + return mItemList.size(); +} + +bool LLScrollListCtrl::hasSelectedItem() const +{ + item_list::iterator iter; + for (iter = mItemList.begin(); iter < mItemList.end(); ) + { + LLScrollListItem* itemp = *iter; + if (itemp && itemp->getSelected()) + { + return true; + } + iter++; + } + return false; +} + +// virtual LLScrolListInterface function (was deleteAllItems) +void LLScrollListCtrl::clearRows() +{ + std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); + mItemList.clear(); + //mItemCount = 0; + + // Scroll the bar back up to the top. + mScrollbar->setDocParams(0, 0); + + mScrollLines = 0; + mLastSelected = NULL; + updateLayout(); + mDirty = false; +} + + +LLScrollListItem* LLScrollListCtrl::getFirstSelected() const +{ + item_list::const_iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getSelected()) + { + return item; + } + } + return NULL; +} + +std::vector LLScrollListCtrl::getAllSelected() const +{ + std::vector ret; + item_list::const_iterator iter; + for(iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getSelected()) + { + ret.push_back(item); + } + } + return ret; +} + +S32 LLScrollListCtrl::getNumSelected() const +{ + S32 numSelected = 0; + + for(item_list::const_iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter) + { + LLScrollListItem* item = *iter; + if (item->getSelected()) + { + ++numSelected; + } + } + + return numSelected; +} + +S32 LLScrollListCtrl::getFirstSelectedIndex() const +{ + S32 CurSelectedIndex = 0; + + // make sure sort is up to date before returning an index + updateSort(); + + item_list::const_iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getSelected()) + { + return CurSelectedIndex; + } + CurSelectedIndex++; + } + + return -1; +} + +LLScrollListItem* LLScrollListCtrl::getFirstData() const +{ + if (mItemList.size() == 0) + { + return NULL; + } + return mItemList[0]; +} + +LLScrollListItem* LLScrollListCtrl::getLastData() const +{ + if (mItemList.size() == 0) + { + return NULL; + } + return mItemList[mItemList.size() - 1]; +} + +std::vector LLScrollListCtrl::getAllData() const +{ + std::vector ret; + item_list::const_iterator iter; + for(iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + ret.push_back(item); + } + return ret; +} + +// returns first matching item +LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const +{ + std::string string_val = sd.asString(); + + item_list::const_iterator iter; + for(iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + // assumes string representation is good enough for comparison + if (item->getValue().asString() == string_val) + { + return item; + } + } + return NULL; +} + + +void LLScrollListCtrl::reshape( S32 width, S32 height, bool called_from_parent ) +{ + LLUICtrl::reshape( width, height, called_from_parent ); + + updateLayout(); +} + +void LLScrollListCtrl::updateLayout() +{ + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + // reserve room for column headers, if needed + S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); + mItemListRect.setOriginAndSize( + mBorderThickness, + mBorderThickness, + getRect().getWidth() - 2 * mBorderThickness, + getRect().getHeight() - (2 * mBorderThickness ) - heading_size ); + + if (mCommentTextView == NULL) + { + mCommentTextView = getChildView("comment_text"); + } + + mCommentTextView->setShape(mItemListRect); + + // how many lines of content in a single "page" + S32 page_lines = getLinesPerPage(); + + bool scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight(); + if (scrollbar_visible) + { + // provide space on the right for scrollbar + mItemListRect.mRight = getRect().getWidth() - mBorderThickness - scrollbar_size; + } + + mScrollbar->setOrigin(getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom); + mScrollbar->reshape(scrollbar_size, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); + mScrollbar->setPageSize(page_lines); + mScrollbar->setDocSize( getItemCount() ); + mScrollbar->setVisible(scrollbar_visible); + + dirtyColumns(); +} + +// Attempt to size the control to show all items. +// Do not make larger than width or height. +void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height) +{ + S32 height = llmin( getRequiredRect().getHeight(), max_height ); + if(mPageLines) + height = llmin( mPageLines * mLineHeight + 2*mBorderThickness + (mDisplayColumnHeaders ? mHeadingHeight : 0), height ); + + S32 width = getRect().getWidth(); + + reshape( width, height ); +} + + +LLRect LLScrollListCtrl::getRequiredRect() +{ + S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); + S32 height = (mLineHeight * getItemCount()) + + (2 * mBorderThickness ) + + heading_size; + S32 width = getRect().getWidth(); + + return LLRect(0, height, width, 0); +} + + +bool LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, bool requires_column ) +{ + bool not_too_big = getItemCount() < mMaxItemCount; + if (not_too_big) + { + switch( pos ) + { + case ADD_TOP: + mItemList.push_front(item); + setNeedsSort(); + break; + + case ADD_DEFAULT: + case ADD_BOTTOM: + mItemList.push_back(item); + setNeedsSort(); + break; + + default: + llassert(0); + mItemList.push_back(item); + setNeedsSort(); + break; + } + + // create new column on demand + if (mColumns.empty() && requires_column) + { + LLScrollListColumn::Params col_params; + col_params.name = "default_column"; + col_params.header.label = ""; + col_params.width.dynamic_width = true; + addColumn(col_params); + } + + S32 num_cols = item->getNumColumns(); + S32 i = 0; + for (LLScrollListCell* cell = item->getColumn(i); i < num_cols; cell = item->getColumn(++i)) + { + if (i >= (S32)mColumnsIndexed.size()) break; + + cell->setWidth(mColumnsIndexed[i]->getWidth()); + } + + updateLineHeightInsert(item); + + updateLayout(); + } + + return not_too_big; +} + +// NOTE: This is *very* expensive for large lists, especially when we are dirtying the list every frame +// while receiving a long list of names. +// *TODO: Use bookkeeping to make this an incramental cost with item additions +S32 LLScrollListCtrl::calcMaxContentWidth() +{ + const S32 HEADING_TEXT_PADDING = 25; + const S32 COLUMN_TEXT_PADDING = 10; + + S32 max_item_width = 0; + + ordered_columns_t::iterator column_itor; + for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) + { + LLScrollListColumn* column = *column_itor; + if (!column) continue; + + if (mColumnWidthsDirty) + { + // update max content width for this column, by looking at all items + column->mMaxContentWidth = column->mHeader ? LLFontGL::getFontSansSerifSmall()->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); + if (!cellp) continue; + + column->mMaxContentWidth = llmax(LLFontGL::getFontSansSerifSmall()->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + } + } + max_item_width += column->mMaxContentWidth; + } + mColumnWidthsDirty = false; + + return max_item_width; +} + +bool LLScrollListCtrl::updateColumnWidths() +{ + bool width_changed = false; + ordered_columns_t::iterator column_itor; + for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) + { + LLScrollListColumn* column = *column_itor; + if (!column) continue; + + // update column width + S32 new_width = 0; + if (column->mRelWidth >= 0) + { + new_width = (S32)ll_round(column->mRelWidth*mItemListRect.getWidth()); + } + else if (column->mDynamicWidth) + { + new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns; + } + else + { + new_width = column->getWidth(); + } + + if (column->getWidth() != new_width) + { + column->setWidth(new_width); + width_changed = true; + } + } + return width_changed; +} + +// Line height is the max height of all the cells in all the items. +void LLScrollListCtrl::updateLineHeight() +{ + mLineHeight = 0; + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + S32 num_cols = itemp->getNumColumns(); + S32 i = 0; + for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) + { + mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding ); + } + } +} + +// when the only change to line height is from an insert, we needn't scan the entire list +void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp) +{ + S32 num_cols = itemp->getNumColumns(); + S32 i = 0; + for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) + { + mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding ); + } +} + + +void LLScrollListCtrl::updateColumns(bool force_update) +{ + if (!mColumnsDirty && !force_update) + return; + + mColumnsDirty = false; + + bool columns_changed_width = updateColumnWidths(); + + // update column headers + std::vector::iterator column_ordered_it; + S32 left = mItemListRect.mLeft; + LLScrollColumnHeader* last_header = NULL; + for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it) + { + LLScrollListColumn* column = *column_ordered_it; + if (!column || column->getWidth() < 0) + { + // skip hidden columns + continue; + } + + if (column->mHeader) + { + column->mHeader->updateResizeBars(); + + last_header = column->mHeader; + S32 top = mItemListRect.mTop; + S32 right = left + column->getWidth(); + + if (column->mIndex != (S32)mColumnsIndexed.size()-1) + { + right += mColumnPadding; + } + right = llmax(left, llmin(mItemListRect.getWidth(), right)); + S32 header_width = right - left; + + last_header->reshape(header_width, mHeadingHeight); + last_header->translate( + left - last_header->getRect().mLeft, + top - last_header->getRect().mBottom); + last_header->setVisible(mDisplayColumnHeaders && header_width > 0); + left = right; + } + } + + bool header_changed_width = false; + // expand last column header we encountered to full list width + if (last_header) + { + S32 old_width = last_header->getColumn()->getWidth(); + S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft); + last_header->reshape(new_width, last_header->getRect().getHeight()); + last_header->setVisible(mDisplayColumnHeaders && new_width > 0); + if (old_width != new_width) + { + last_header->getColumn()->setWidth(new_width); + header_changed_width = true; + } + } + + // propagate column widths to individual cells + if (columns_changed_width || force_update) + { + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + S32 num_cols = itemp->getNumColumns(); + S32 i = 0; + for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) + { + if (i >= (S32)mColumnsIndexed.size()) break; + + cell->setWidth(mColumnsIndexed[i]->getWidth()); + } + } + } + else if (header_changed_width) + { + item_list::iterator iter; + S32 index = last_header->getColumn()->mIndex; // Not always identical to last column! + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + LLScrollListCell* cell = itemp->getColumn(index); + if (cell) + { + cell->setWidth(last_header->getColumn()->getWidth()); + } + } + } +} + +void LLScrollListCtrl::setHeadingHeight(S32 heading_height) +{ + mHeadingHeight = heading_height; + + updateLayout(); + +} +void LLScrollListCtrl::setPageLines(S32 new_page_lines) +{ + mPageLines = new_page_lines; + + updateLayout(); +} + +bool LLScrollListCtrl::selectFirstItem() +{ + bool success = false; + + // our $%&@#$()^%#$()*^ iterators don't let us check against the first item inside out iteration + bool first_item = true; + + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + if( first_item && itemp->getEnabled() ) + { + if (!itemp->getSelected()) + { + switch (mSelectionType) + { + case CELL: + selectItem(itemp, 0); + break; + case HEADER: + case ROW: + selectItem(itemp, -1); + } + } + success = true; + mOriginalSelection = 0; + } + else + { + deselectItem(itemp); + } + first_item = false; + } + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + return success; +} + +// Deselects all other items +// virtual +bool LLScrollListCtrl::selectNthItem( S32 target_index ) +{ + return selectItemRange(target_index, target_index); +} + +// virtual +bool LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) +{ + if (mItemList.empty()) + { + return false; + } + + // make sure sort is up to date + updateSort(); + + S32 listlen = (S32)mItemList.size(); + first_index = llclamp(first_index, 0, listlen-1); + + if (last_index < 0) + last_index = listlen-1; + else + last_index = llclamp(last_index, first_index, listlen-1); + + bool success = false; + S32 index = 0; + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ) + { + LLScrollListItem *itemp = *iter; + if(!itemp) + { + iter = mItemList.erase(iter); + continue ; + } + + if( index >= first_index && index <= last_index ) + { + if( itemp->getEnabled() ) + { + // TODO: support range selection for cells + selectItem(itemp, -1, false); + success = true; + } + } + else + { + deselectItem(itemp); + } + index++; + iter++ ; + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + mSearchString.clear(); + + return success; +} + + +void LLScrollListCtrl::swapWithNext(S32 index) +{ + if (index >= ((S32)mItemList.size() - 1)) + { + // At end of list, doesn't do anything + return; + } + updateSort(); + LLScrollListItem *cur_itemp = mItemList[index]; + mItemList[index] = mItemList[index + 1]; + mItemList[index + 1] = cur_itemp; +} + + +void LLScrollListCtrl::swapWithPrevious(S32 index) +{ + if (index <= 0) + { + // At beginning of list, don't do anything + } + + updateSort(); + LLScrollListItem *cur_itemp = mItemList[index]; + mItemList[index] = mItemList[index - 1]; + mItemList[index - 1] = cur_itemp; +} + + +void LLScrollListCtrl::deleteSingleItem(S32 target_index) +{ + if (target_index < 0 || target_index >= (S32)mItemList.size()) + { + return; + } + + updateSort(); + + LLScrollListItem *itemp; + itemp = mItemList[target_index]; + if (itemp == mLastSelected) + { + mLastSelected = NULL; + } + delete itemp; + mItemList.erase(mItemList.begin() + target_index); + dirtyColumns(); +} + +//FIXME: refactor item deletion +void LLScrollListCtrl::deleteItems(const LLSD& sd) +{ + item_list::iterator iter; + for (iter = mItemList.begin(); iter < mItemList.end(); ) + { + LLScrollListItem* itemp = *iter; + if (itemp->getValue().asString() == sd.asString()) + { + if (itemp == mLastSelected) + { + mLastSelected = NULL; + } + delete itemp; + iter = mItemList.erase(iter); + } + else + { + iter++; + } + } + + dirtyColumns(); +} + +void LLScrollListCtrl::deleteSelectedItems() +{ + item_list::iterator iter; + for (iter = mItemList.begin(); iter < mItemList.end(); ) + { + LLScrollListItem* itemp = *iter; + if (itemp->getSelected()) + { + delete itemp; + iter = mItemList.erase(iter); + } + else + { + iter++; + } + } + mLastSelected = NULL; + dirtyColumns(); +} + +void LLScrollListCtrl::clearHighlightedItems() +{ + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter) + { + (*iter)->setHighlighted(false); + } +} + +void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index) +{ + if (mHighlightedItem != target_index) + { + if (mHighlightedItem >= 0 && mHighlightedItem < mItemList.size()) + { + mItemList[mHighlightedItem]->setHoverCell(-1); + } + mHighlightedItem = target_index; + } +} + +S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) +{ + item_list::iterator iter; + S32 count = 0; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + uuid_vec_t::iterator iditr; + for(iditr = ids.begin(); iditr != ids.end(); ++iditr) + { + if (item->getEnabled() && (item->getUUID() == (*iditr))) + { + // TODO: support multiple selection for cells + selectItem(item, -1, false); + ++count; + break; + } + } + if(ids.end() != iditr) ids.erase(iditr); + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + return count; +} + +S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const +{ + updateSort(); + + S32 index = 0; + item_list::const_iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + if (target_item == itemp) + { + return index; + } + index++; + } + return -1; +} + +S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const +{ + updateSort(); + + S32 index = 0; + item_list::const_iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + if (target_id == itemp->getUUID()) + { + return index; + } + index++; + } + return -1; +} + +void LLScrollListCtrl::selectPrevItem( bool extend_selection) +{ + LLScrollListItem* prev_item = NULL; + + if (!getFirstSelected()) + { + // select last item + selectNthItem(getItemCount() - 1); + } + else + { + updateSort(); + + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* cur_item = *iter; + + if (cur_item->getSelected()) + { + if (prev_item) + { + selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection); + } + else + { + reportInvalidInput(); + } + break; + } + + // don't allow navigation to disabled elements + prev_item = cur_item->getEnabled() ? cur_item : prev_item; + } + } + + if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement)) + { + commitIfChanged(); + } + + mSearchString.clear(); +} + + +void LLScrollListCtrl::selectNextItem( bool extend_selection) +{ + LLScrollListItem* next_item = NULL; + + if (!getFirstSelected()) + { + selectFirstItem(); + } + else + { + updateSort(); + + item_list::reverse_iterator iter; + for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++) + { + LLScrollListItem* cur_item = *iter; + + if (cur_item->getSelected()) + { + if (next_item) + { + selectItem(next_item, cur_item->getSelectedCell(), !extend_selection); + } + else + { + reportInvalidInput(); + } + break; + } + + // don't allow navigation to disabled items + next_item = cur_item->getEnabled() ? cur_item : next_item; + } + } + + if (mCommitOnKeyboardMovement) + { + onCommit(); + } + + mSearchString.clear(); +} + + + +void LLScrollListCtrl::deselectAllItems(bool no_commit_on_change) +{ + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + deselectItem(item); + } + + if (mCommitOnSelectionChange && !no_commit_on_change) + { + commitIfChanged(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Use this to add comment text such as "Searching", which ignores column settings of list + +void LLScrollListCtrl::setCommentText(const std::string& comment_text) +{ + getChild("comment_text")->setValue(comment_text); +} + +LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos) +{ + LLScrollListItem::Params separator_params; + separator_params.enabled(false); + LLScrollListCell::Params column_params; + column_params.type = "icon"; + column_params.value = "menu_separator"; + column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); + column_params.font_halign = LLFontGL::HCENTER; + separator_params.columns.add(column_params); + return addRow( separator_params, pos ); +} + +// Selects first enabled item of the given name. +// Returns false if item not found. +// Calls getItemByLabel in order to combine functionality +bool LLScrollListCtrl::selectItemByLabel(const std::string& label, bool case_sensitive, S32 column/* = 0*/) +{ + deselectAllItems(true); // ensure that no stale items are selected, even if we don't find a match + LLScrollListItem* item = getItemByLabel(label, case_sensitive, column); + + bool found = NULL != item; + if (found) + { + selectItem(item, -1); + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + return found; +} + +LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, bool case_sensitive, S32 column) +{ + if (label.empty()) //RN: assume no empty items + { + return NULL; + } + + std::string target_text = label; + if (!case_sensitive) + { + LLStringUtil::toLower(target_text); + } + + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + std::string item_text = item->getColumn(column)->getValue().asString(); // Only select enabled items with matching names + if (!case_sensitive) + { + LLStringUtil::toLower(item_text); + } + if(item_text == target_text) + { + return item; + } + } + return NULL; +} + + +bool LLScrollListCtrl::selectItemByPrefix(const std::string& target, bool case_sensitive, S32 column) +{ + return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive, column); +} + +// Selects first enabled item that has a name where the name's first part matched the target string. +// Returns false if item not found. +bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target, bool case_sensitive, S32 column) +{ + bool found = false; + + LLWString target_trimmed( target ); + S32 target_len = target_trimmed.size(); + + if( 0 == target_len ) + { + // Is "" a valid choice? + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + // Only select enabled items with matching names + LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column); + bool select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : false; + if (select) + { + selectItem(item, -1); + found = true; + break; + } + } + } + else + { + if (!case_sensitive) + { + // do comparisons in lower case + LLWStringUtil::toLower(target_trimmed); + } + + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + + // Only select enabled items with matching names + LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column); + if (!cellp) + { + continue; + } + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); + if (!case_sensitive) + { + LLWStringUtil::toLower(item_label); + } + // remove extraneous whitespace from searchable label + LLWString trimmed_label = item_label; + LLWStringUtil::trim(trimmed_label); + + bool select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0; + + if (select) + { + // find offset of matching text (might have leading whitespace) + S32 offset = item_label.find(target_trimmed); + cellp->highlightText(offset, target_trimmed.size()); + selectItem(item, -1); + found = true; + break; + } + } + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + return found; +} + +U32 LLScrollListCtrl::searchItems(const std::string& substring, bool case_sensitive, bool focus) +{ + return searchItems(utf8str_to_wstring(substring), case_sensitive, focus); +} + +U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitive, bool focus) +{ + U32 found = 0; + + LLWString substring_trimmed(substring); + S32 len = substring_trimmed.size(); + + if (0 == len) + { + // at the moment search for empty element is not supported + return 0; + } + else + { + deselectAllItems(true); + if (!case_sensitive) + { + // do comparisons in lower case + LLWStringUtil::toLower(substring_trimmed); + } + + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + // Only select enabled items with matching names + if (!item->getEnabled()) + { + continue; + } + LLScrollListCell* cellp = item->getColumn(getSearchColumn()); + if (!cellp) + { + continue; + } + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); + if (!case_sensitive) + { + LLWStringUtil::toLower(item_label); + } + // remove extraneous whitespace from searchable label + LLWStringUtil::trim(item_label); + + size_t found_iter = item_label.find(substring_trimmed); + + if (found_iter != std::string::npos) + { + // find offset of matching text + cellp->highlightText(found_iter, substring_trimmed.size()); + selectItem(item, -1, false); + + found++; + + if (!mAllowMultipleSelection) + { + break; + } + } + } + } + + if (focus && found != 0) + { + mNeedsScroll = true; + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + return found; +} + +const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const +{ + LLScrollListItem* item; + + item = getFirstSelected(); + if (item) + { + return item->getColumn(column)->getValue().asString(); + } + + return LLStringUtil::null; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which +// has an associated, unique UUID, and only one of which can be selected at a time. + +LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, bool enabled) +{ + if (getItemCount() < mMaxItemCount) + { + LLScrollListItem::Params item_p; + item_p.enabled(enabled); + item_p.value(id); + item_p.columns.add().value(item_text).type("text"); + + return addRow( item_p, pos ); + } + return NULL; +} + +// Select the line or lines that match this UUID +bool LLScrollListCtrl::selectByID( const LLUUID& id ) +{ + return selectByValue( LLSD(id) ); +} + +bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected) +{ + bool found = false; + + if (selected && !mAllowMultipleSelection) deselectAllItems(true); + + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getEnabled()) + { + if (value.isBinary()) + { + if (item->getValue().isBinary()) + { + LLSD::Binary data1 = value.asBinary(); + LLSD::Binary data2 = item->getValue().asBinary(); + found = std::equal(data1.begin(), data1.end(), data2.begin()); + } + } + else + { + found = item->getValue().asString() == value.asString(); + } + + if (found) + { + if (selected) + { + selectItem(item, -1); + } + else + { + deselectItem(item); + } + break; + } + } + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + return found; +} + +bool LLScrollListCtrl::isSelected(const LLSD& value) const +{ + item_list::const_iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getValue().asString() == value.asString()) + { + return item->getSelected(); + } + } + return false; +} + +LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() const +{ + LLScrollListItem* item = getFirstSelected(); + + if (item) + { + return item->getUUID(); + } + + return LLUUID::null; +} + +LLSD LLScrollListCtrl::getSelectedValue() +{ + LLScrollListItem* item = getFirstSelected(); + + if (item) + { + return item->getValue(); + } + else + { + return LLSD(); + } +} + +void LLScrollListCtrl::drawItems() +{ + S32 x = mItemListRect.mLeft; + S32 y = mItemListRect.mTop - mLineHeight; + + // allow for partial line at bottom + S32 num_page_lines = getLinesPerPage(); + + LLRect item_rect; + + LLGLSUIDefault gls_ui; + + F32 alpha = getDrawContext().mAlpha; + + { + LLLocalClipRect clip(mItemListRect); + + S32 cur_y = y; + + S32 max_columns = 0; + + LLColor4 highlight_color = LLColor4::white; // ex: text inside cells + static LLUICachedControl type_ahead_timeout ("TypeAheadTimeout", 0); + highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout(), 0.4f, 0.f); + + S32 first_line = mScrollLines; + S32 last_line = llmin((S32)mItemList.size() - 1, mScrollLines + getLinesPerPage()); + + if (first_line >= mItemList.size()) + { + return; + } + item_list::iterator iter; + for (S32 line = first_line; line <= last_line; line++) + { + LLScrollListItem* item = mItemList[line]; + + item_rect.setOriginAndSize( + x, + cur_y, + mItemListRect.getWidth(), + mLineHeight ); + item->setRect(item_rect); + + max_columns = llmax(max_columns, item->getNumColumns()); + + LLColor4 fg_color; + LLColor4 hover_color(LLColor4::transparent); + LLColor4 select_color(LLColor4::transparent); + + if( mScrollLines <= line && line < mScrollLines + num_page_lines ) + { + fg_color = (item->getEnabled() ? mFgUnselectedColor.get() : mFgDisabledColor.get()); + if( item->getSelected() && mCanSelect) + { + if(item->getHighlighted()) // if it's highlighted, average the colors + { + select_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f); + } + else // otherwise just select-highlight it + { + select_color = mBgSelectedColor.get(); + } + + fg_color = (item->getEnabled() ? mFgSelectedColor.get() : mFgDisabledColor.get()); + } + if (mHighlightedItem == line && mCanSelect) + { + if(item->getHighlighted()) // if it's highlighted, average the colors + { + hover_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f); + } + else // otherwise just hover-highlight it + { + hover_color = mHoveredColor.get(); + } + } + else if (item->getHighlighted()) + { + hover_color = mHighlightedColor.get(); + } + else + { + if (mDrawStripes && (line % 2 == 0) && (max_columns > 1)) + { + hover_color = mBgStripeColor.get(); + } + } + + if (!item->getEnabled()) + { + hover_color = mBgReadOnlyColor.get(); + } + + item->draw(item_rect, fg_color % alpha, hover_color% alpha, select_color% alpha, highlight_color % alpha, mColumnPadding); + + cur_y -= mLineHeight; + } + } + } +} + + +void LLScrollListCtrl::draw() +{ + LLLocalClipRect clip(getLocalRect()); + + // if user specifies sort, make sure it is maintained + updateSort(); + + if (mNeedsScroll) + { + scrollToShowSelected(); + mNeedsScroll = false; + } + LLRect background(0, getRect().getHeight(), getRect().getWidth(), 0); + // Draw background + if (mBackgroundVisible) + { + F32 alpha = getCurrentTransparency(); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha ); + } + + updateColumns(); + + getChildView("comment_text")->setVisible(mItemList.empty()); + + drawItems(); + + if (mBorder) + { + mBorder->setKeyboardFocusHighlight(hasFocus()); + } + + LLUICtrl::draw(); +} + +void LLScrollListCtrl::setEnabled(bool enabled) +{ + mCanSelect = enabled; + setTabStop(enabled); + mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize()); +} + +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; +} + +bool LLScrollListCtrl::handleScrollHWheel(S32 x, S32 y, S32 clicks) +{ + bool handled = false; + // Pretend the mouse is over the scrollbar + handled = mScrollbar->handleScrollHWheel( 0, 0, clicks ); + + if (mMouseWheelOpaque) + { + return true; + } + + return handled; +} + +// *NOTE: Requires a valid row_index and column_index +LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index) +{ + LLRect cell_rect; + S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; + S32 rect_bottom = getRowOffsetFromIndex(row_index); + LLScrollListColumn* columnp = getColumn(column_index); + cell_rect.setOriginAndSize(rect_left, rect_bottom, + /*rect_left + */columnp->getWidth(), mLineHeight); + return cell_rect; +} + +bool LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) +{ + S32 column_index = getColumnIndexFromOffset(x); + LLScrollListColumn* columnp = getColumn(column_index); + + if (columnp == NULL) return false; + + bool handled = false; + // show tooltip for full name of hovered item if it has been truncated + LLScrollListItem* hit_item = hitItem(x, y); + if (hit_item) + { + LLScrollListCell* hit_cell = hit_item->getColumn(column_index); + if (!hit_cell) return false; + if (hit_cell + && hit_cell->isText() + && hit_cell->needsToolTip()) + { + S32 row_index = getItemIndex(hit_item); + LLRect cell_rect = getCellRect(row_index, column_index); + // Convert rect local to screen coordinates + LLRect sticky_rect; + localRectToScreen(cell_rect, &sticky_rect); + + // display tooltip exactly over original cell, in same font + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(hit_cell->getToolTip()) + .font(LLFontGL::getFontEmojiSmall()) + .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 6)) + .delay_time(0.2f) + .sticky_rect(sticky_rect)); + } + handled = true; + } + + // otherwise, look for a tooltip associated with this column + LLScrollColumnHeader* headerp = columnp->mHeader; + if (headerp && !handled) + { + handled = headerp->handleToolTip(x, y, mask); + } + + return handled; +} + +bool LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) +{ + if (!mCanSelect) return false; + + bool selection_changed = false; + + LLScrollListItem* hit_item = hitItem(x, y); + + if( hit_item ) + { + if( mAllowMultipleSelection ) + { + if (mask & MASK_SHIFT) + { + if (mLastSelected == NULL) + { + selectItem(hit_item, getColumnIndexFromOffset(x)); + } + else + { + // Select everthing between mLastSelected and hit_item + bool selecting = false; + item_list::iterator itor; + // If we multiselect backwards, we'll stomp on mLastSelected, + // meaning that we never stop selecting until hitting max or + // the end of the list. + LLScrollListItem* lastSelected = mLastSelected; + for (itor = mItemList.begin(); itor != mItemList.end(); ++itor) + { + if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable) + { + if(mOnMaximumSelectCallback) + { + mOnMaximumSelectCallback(); + } + break; + } + LLScrollListItem *item = *itor; + if (item == hit_item || item == lastSelected) + { + selectItem(item, getColumnIndexFromOffset(x), false); + selecting = !selecting; + if (hit_item == lastSelected) + { + // stop selecting now, since we just clicked on our last selected item + selecting = false; + } + } + if (selecting) + { + selectItem(item, getColumnIndexFromOffset(x), false); + } + } + } + } + else if (mask & MASK_CONTROL) + { + if (hit_item->getSelected()) + { + deselectItem(hit_item); + } + else + { + if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) + { + selectItem(hit_item, getColumnIndexFromOffset(x), false); + } + else + { + if(mOnMaximumSelectCallback) + { + mOnMaximumSelectCallback(); + } + } + } + } + else + { + deselectAllItems(true); + selectItem(hit_item, getColumnIndexFromOffset(x)); + } + } + else + { + selectItem(hit_item, getColumnIndexFromOffset(x)); + } + + selection_changed = mSelectionChanged; + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + // clear search string on mouse operations + mSearchString.clear(); + } + else + { + //mLastSelected = NULL; + //deselectAllItems(true); + } + + return selection_changed; +} + + +bool LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = childrenHandleMouseDown(x, y, mask) != NULL; + + if( !handled ) + { + // set keyboard focus first, in case click action wants to move focus elsewhere + setFocus(true); + + // clear selection changed flag because user is starting a selection operation + mSelectionChanged = false; + + handleClick(x, y, mask); + } + + return true; +} + +bool LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (hasMouseCapture()) + { + // release mouse capture immediately so + // scroll to show selected logic will work + gFocusMgr.setMouseCapture(NULL); + if(mask == MASK_NONE) + { + selectItemAt(x, y, mask); + mNeedsScroll = true; + } + } + + // always commit when mouse operation is completed inside list + if (mItemListRect.pointInRect(x,y)) + { + mDirty = mDirty || mSelectionChanged; + mSelectionChanged = false; + onCommit(); + } + + return LLUICtrl::handleMouseUp(x, y, mask); +} + +// virtual +bool LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLScrollListItem *item = hitItem(x, y); + if (item) + { + // check to see if we have a UUID for this row + std::string id = item->getValue().asString(); + LLUUID uuid(id); + if (! uuid.isNull() && mContextMenuType != MENU_NONE) + { + // set up the callbacks for all of the avatar/group menu items + // (N.B. callbacks don't take const refs as id is local scope) + bool is_group = (mContextMenuType == MENU_GROUP); + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Url.ShowProfile", boost::bind(&LLScrollListCtrl::showProfile, id, is_group)); + registrar.add("Url.SendIM", boost::bind(&LLScrollListCtrl::sendIM, id)); + registrar.add("Url.AddFriend", boost::bind(&LLScrollListCtrl::addFriend, id)); + registrar.add("Url.RemoveFriend", boost::bind(&LLScrollListCtrl::removeFriend, id)); + registrar.add("Url.ReportAbuse", boost::bind(&LLScrollListCtrl::reportAbuse, id, is_group)); + registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group)); + registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group)); + registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group)); + + // create the context menu from the XUI file and display it + std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml"; + auto menu = mPopupMenuHandle.get(); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } + llassert(LLMenuGL::sMenuContainer != NULL); + menu = LLUICtrlFactory::getInstance()->createFromFile( + menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (menu) + { + mPopupMenuHandle = menu->getHandle(); + if (mIsFriendSignal) + { + bool isFriend = *(*mIsFriendSignal)(uuid); + LLView* addFriendButton = menu->getChild("add_friend"); + LLView* removeFriendButton = menu->getChild("remove_friend"); + + if (addFriendButton && removeFriendButton) + { + addFriendButton->setEnabled(!isFriend); + removeFriendButton->setEnabled(isFriend); + } + } + + menu->show(x, y); + LLMenuGL::showPopup(this, menu, x, y); + return true; + } + } + return LLUICtrl::handleRightMouseDown(x, y, mask); + } + return false; +} + +void LLScrollListCtrl::showProfile(std::string id, bool is_group) +{ + // show the resident's profile or the group profile + std::string sltype = is_group ? "group" : "agent"; + std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; + LLUrlAction::showProfile(slurl); +} + +void LLScrollListCtrl::sendIM(std::string id) +{ + // send im to the resident + std::string slurl = "secondlife:///app/agent/" + id + "/about"; + LLUrlAction::sendIM(slurl); +} + +void LLScrollListCtrl::addFriend(std::string id) +{ + // add resident to friends list + std::string slurl = "secondlife:///app/agent/" + id + "/about"; + LLUrlAction::addFriend(slurl); +} + +void LLScrollListCtrl::removeFriend(std::string id) +{ + std::string slurl = "secondlife:///app/agent/" + id + "/about"; + LLUrlAction::removeFriend(slurl); +} + +void LLScrollListCtrl::reportAbuse(std::string id, bool is_group) +{ + if (!is_group) + { + std::string slurl = "secondlife:///app/agent/" + id + "/about"; + LLUrlAction::reportAbuse(slurl); + } +} + +void LLScrollListCtrl::showNameDetails(std::string id, bool is_group) +{ + // open the resident's details or the group details + std::string sltype = is_group ? "group" : "agent"; + std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; + LLUrlAction::clickAction(slurl, true); +} + +void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group) +{ + // copy the name of the avatar or group to the clipboard + std::string name; + if (is_group) + { + gCacheName->getGroupName(LLUUID(id), name); + } + else + { + LLAvatarName av_name; + LLAvatarNameCache::get(LLUUID(id), &av_name); + name = av_name.getAccountName(); + } + LLUrlAction::copyURLToClipboard(name); +} + +void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group) +{ + // copy a SLURL for the avatar or group to the clipboard + std::string sltype = is_group ? "group" : "agent"; + std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; + LLUrlAction::copyURLToClipboard(slurl); +} + +bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + //bool handled = false; + bool handled = handleClick(x, y, mask); + + if (!handled) + { + // Offer the click to the children, even if we aren't enabled + // so the scroll bars will work. + if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) + { + // Run the callback only if an item is being double-clicked. + if( mCanSelect && hitItem(x, y) && mOnDoubleClickCallback ) + { + mOnDoubleClickCallback(); + } + } + } + + return true; +} + +bool LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) +{ + // which row was clicked on? + LLScrollListItem* hit_item = hitItem(x, y); + if (!hit_item) return false; + + // get appropriate cell from that row + S32 column_index = getColumnIndexFromOffset(x); + LLScrollListCell* hit_cell = hit_item->getColumn(column_index); + if (!hit_cell) return false; + + // if cell handled click directly (i.e. clicked on an embedded checkbox) + if (hit_cell->handleClick()) + { + // if item not currently selected, select it + if (!hit_item->getSelected()) + { + selectItemAt(x, y, mask); + gFocusMgr.setMouseCapture(this); + mNeedsScroll = true; + } + + // propagate state of cell to rest of selected column + { + // propagate value of this cell to other selected items + // and commit the respective widgets + LLSD item_value = hit_cell->getValue(); + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getSelected()) + { + LLScrollListCell* cellp = item->getColumn(column_index); + cellp->setValue(item_value); + cellp->onCommit(); + if (mLastSelected == NULL) + { + break; + } + } + } + //FIXME: find a better way to signal cell changes + onCommit(); + } + // eat click (e.g. do not trigger double click callback) + return true; + } + else + { + // treat this as a normal single item selection + selectItemAt(x, y, mask); + gFocusMgr.setMouseCapture(this); + mNeedsScroll = true; + // do not eat click (allow double click callback) + return false; + } +} + +LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) +{ + // Excludes disabled items. + LLScrollListItem* hit_item = NULL; + + updateSort(); + + LLRect item_rect; + item_rect.setLeftTopAndSize( + mItemListRect.mLeft, + mItemListRect.mTop, + mItemListRect.getWidth(), + mLineHeight ); + + // allow for partial line at bottom + S32 num_page_lines = getLinesPerPage(); + + S32 line = 0; + item_list::iterator iter; + for(iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if( mScrollLines <= line && line < mScrollLines + num_page_lines ) + { + if( item->getEnabled() && item_rect.pointInRect( x, y ) ) + { + hit_item = item; + break; + } + + item_rect.translate(0, -mLineHeight); + } + line++; + } + + return hit_item; +} + +S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x) +{ + // which column did we hit? + S32 left = 0; + S32 right = 0; + S32 width = 0; + S32 column_index = 0; + + ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); + ordered_columns_t::const_iterator end = mColumnsIndexed.end(); + for ( ; iter != end; ++iter) + { + width = (*iter)->getWidth() + mColumnPadding; + right += width; + if (left <= x && x < right ) + { + break; + } + + // set left for next column as right of current column + left = right; + column_index++; + } + + return llclamp(column_index, 0, getNumColumns() - 1); +} + + +S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index) +{ + S32 column_offset = 0; + ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); + ordered_columns_t::const_iterator end = mColumnsIndexed.end(); + for ( ; iter != end; ++iter) + { + if (index-- <= 0) + { + return column_offset; + } + column_offset += (*iter)->getWidth() + mColumnPadding; + } + + // when running off the end, return the rightmost pixel + return mItemListRect.mRight; +} + +S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index) +{ + S32 row_bottom = (mItemListRect.mTop - ((index - mScrollLines + 1) * mLineHeight) ); + return row_bottom; +} + + +bool LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) +{ + bool handled = false; + + if (hasMouseCapture()) + { + if(mask == MASK_NONE) + { + selectItemAt(x, y, mask); + mNeedsScroll = true; + } + } + else + if (mCanSelect) + { + LLScrollListItem* item = hitItem(x, y); + if (item) + { + mouseOverHighlightNthItem(getItemIndex(item)); + switch (mSelectionType) + { + case CELL: + item->setHoverCell(getColumnIndexFromOffset(x)); + break; + case HEADER: + { + S32 cell = getColumnIndexFromOffset(x); + if (cell > 0) + { + item->setHoverCell(cell); + } + else + { + item->setHoverCell(-1); + } + break; + } + case ROW: + break; + } + } + else + { + mouseOverHighlightNthItem(-1); + } + } + + handled = LLUICtrl::handleHover( x, y, mask ); + + return handled; +} + +void LLScrollListCtrl::onMouseLeave(S32 x, S32 y, MASK mask) +{ + // clear mouse highlight + mouseOverHighlightNthItem(-1); +} + +bool LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) +{ + bool handled = false; + + // not called from parent means we have keyboard focus or a child does + if (mCanSelect) + { + if (mask == MASK_NONE) + { + switch(key) + { + case KEY_UP: + if (mAllowKeyboardMovement || hasFocus()) + { + // commit implicit in call + selectPrevItem(false); + mNeedsScroll = true; + handled = true; + } + break; + case KEY_DOWN: + if (mAllowKeyboardMovement || hasFocus()) + { + // commit implicit in call + selectNextItem(false); + mNeedsScroll = true; + handled = true; + } + break; + case KEY_LEFT: + if (mAllowKeyboardMovement || hasFocus()) + { + // TODO: support multi-select + LLScrollListItem *item = getFirstSelected(); + if (item) + { + S32 cell = item->getSelectedCell(); + switch (mSelectionType) + { + case CELL: + if (cell < mColumns.size()) cell++; + break; + case HEADER: + if (cell == -1) cell = 1; + else if (cell > 1 && cell < mColumns.size()) cell++; // skip header + break; + case ROW: + cell = -1; + break; + } + item->setSelectedCell(cell); + handled = true; + } + } + break; + case KEY_RIGHT: + if (mAllowKeyboardMovement || hasFocus()) + { + // TODO: support multi-select + LLScrollListItem *item = getFirstSelected(); + if (item) + { + S32 cell = item->getSelectedCell(); + switch (mSelectionType) + { + case CELL: + if (cell >= 0) cell--; + break; + case HEADER: + if (cell > 1) cell--; + else if (cell == 1) cell = -1; // skip header + break; + case ROW: + cell = -1; + break; + } + item->setSelectedCell(cell); + handled = true; + } + } + break; + case KEY_PAGE_UP: + if (mAllowKeyboardMovement || hasFocus()) + { + selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1)); + mNeedsScroll = true; + if (mCommitOnKeyboardMovement + && !mCommitOnSelectionChange) + { + onCommit(); + } + handled = true; + } + break; + case KEY_PAGE_DOWN: + if (mAllowKeyboardMovement || hasFocus()) + { + selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1)); + mNeedsScroll = true; + if (mCommitOnKeyboardMovement + && !mCommitOnSelectionChange) + { + onCommit(); + } + handled = true; + } + break; + case KEY_HOME: + if (mAllowKeyboardMovement || hasFocus()) + { + selectFirstItem(); + mNeedsScroll = true; + if (mCommitOnKeyboardMovement + && !mCommitOnSelectionChange) + { + onCommit(); + } + handled = true; + } + break; + case KEY_END: + if (mAllowKeyboardMovement || hasFocus()) + { + selectNthItem(getItemCount() - 1); + mNeedsScroll = true; + if (mCommitOnKeyboardMovement + && !mCommitOnSelectionChange) + { + onCommit(); + } + handled = true; + } + break; + case KEY_RETURN: + // JC - Special case: Only claim to have handled it + // if we're the special non-commit-on-move + // type. AND we are visible + if (!mCommitOnKeyboardMovement && mask == MASK_NONE) + { + onCommit(); + mSearchString.clear(); + handled = true; + } + break; + case KEY_BACKSPACE: + mSearchTimer.reset(); + if (mSearchString.size()) + { + mSearchString.erase(mSearchString.size() - 1, 1); + } + if (mSearchString.empty()) + { + if (getFirstSelected()) + { + LLScrollListCell* cellp = getFirstSelected()->getColumn(getSearchColumn()); + if (cellp) + { + cellp->highlightText(0, 0); + } + } + } + else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), false)) + { + mNeedsScroll = true; + // update search string only on successful match + mSearchTimer.reset(); + + if (mCommitOnKeyboardMovement + && !mCommitOnSelectionChange) + { + onCommit(); + } + } + break; + default: + break; + } + } + // TODO: multiple: shift-up, shift-down, shift-home, shift-end, select all + } + + return handled; +} + +bool LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) +{ + if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL + { + return false; + } + + // perform incremental search based on keyboard input + static LLUICachedControl type_ahead_timeout ("TypeAheadTimeout", 0); + if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout) + { + mSearchString.clear(); + } + + // type ahead search is case insensitive + uni_char = LLStringOps::toLower((llwchar)uni_char); + + if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), false)) + { + // update search string only on successful match + mNeedsScroll = true; + mSearchString += uni_char; + mSearchTimer.reset(); + + if (mCommitOnKeyboardMovement + && !mCommitOnSelectionChange) + { + onCommit(); + } + } + // handle iterating over same starting character + else if (isRepeatedChars(mSearchString + (llwchar)uni_char) && !mItemList.empty()) + { + // start from last selected item, in case we previously had a successful match against + // duplicated characters ('AA' matches 'Aaron') + item_list::iterator start_iter = mItemList.begin(); + S32 first_selected = getFirstSelectedIndex(); + + // if we have a selection (> -1) then point iterator at the selected item + if (first_selected > 0) + { + // point iterator to first selected item + start_iter += first_selected; + } + + // start search at first item after current selection + item_list::iterator iter = start_iter; + ++iter; + if (iter == mItemList.end()) + { + iter = mItemList.begin(); + } + + // loop around once, back to previous selection + while(iter != start_iter) + { + LLScrollListItem* item = *iter; + + LLScrollListCell* cellp = item->getColumn(getSearchColumn()); + if (cellp) + { + // Only select enabled items with matching first characters + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); + if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) + { + selectItem(item, -1); + mNeedsScroll = true; + cellp->highlightText(0, 1); + mSearchTimer.reset(); + + if (mCommitOnKeyboardMovement + && !mCommitOnSelectionChange) + { + onCommit(); + } + + break; + } + } + + ++iter; + if (iter == mItemList.end()) + { + iter = mItemList.begin(); + } + } + } + + return true; +} + + +void LLScrollListCtrl::reportInvalidInput() +{ + make_ui_sound("UISndBadKeystroke"); +} + +bool LLScrollListCtrl::isRepeatedChars(const LLWString& string) const +{ + if (string.empty()) + { + return false; + } + + llwchar first_char = string[0]; + + for (U32 i = 0; i < string.size(); i++) + { + if (string[i] != first_char) + { + return false; + } + } + + return true; +} + +void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, bool select_single_item) +{ + if (!itemp) return; + + if (!itemp->getSelected()) + { + if (mLastSelected) + { + LLScrollListCell* cellp = mLastSelected->getColumn(getSearchColumn()); + if (cellp) + { + cellp->highlightText(0, 0); + } + } + if (select_single_item) + { + deselectAllItems(true); + } + itemp->setSelected(true); + switch (mSelectionType) + { + case CELL: + itemp->setSelectedCell(cell); + break; + case HEADER: + itemp->setSelectedCell(cell <= 0 ? -1 : cell); + break; + case ROW: + itemp->setSelectedCell(-1); + break; + } + mLastSelected = itemp; + mSelectionChanged = true; + } +} + +void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) +{ + if (!itemp) return; + + if (itemp->getSelected()) + { + if (mLastSelected == itemp) + { + mLastSelected = NULL; + } + + itemp->setSelected(false); + LLScrollListCell* cellp = itemp->getColumn(getSearchColumn()); + if (cellp) + { + cellp->highlightText(0, 0); + } + mSelectionChanged = true; + } +} + +void LLScrollListCtrl::commitIfChanged() +{ + if (mSelectionChanged) + { + mDirty = true; + mSelectionChanged = false; + onCommit(); + } +} + +struct SameSortColumn +{ + SameSortColumn(S32 column) : mColumn(column) {} + S32 mColumn; + + bool operator()(std::pair sort_column) { return sort_column.first == mColumn; } +}; + +bool LLScrollListCtrl::setSort(S32 column_idx, bool ascending) +{ + LLScrollListColumn* sort_column = getColumn(column_idx); + if (!sort_column) return false; + + sort_column->mSortDirection = ascending ? LLScrollListColumn::ASCENDING : LLScrollListColumn::DESCENDING; + + sort_column_t new_sort_column(column_idx, ascending); + + setNeedsSort(); + + if (mSortColumns.empty()) + { + mSortColumns.push_back(new_sort_column); + return true; + } + else + { + // grab current sort column + sort_column_t cur_sort_column = mSortColumns.back(); + + // remove any existing sort criterion referencing this column + // and add the new one + mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column_idx)), mSortColumns.end()); + mSortColumns.push_back(new_sort_column); + + // did the sort criteria change? + return (cur_sort_column != new_sort_column); + } +} + +S32 LLScrollListCtrl::getLinesPerPage() +{ + //if mPageLines is NOT provided display all item + if (mPageLines) + { + return mPageLines; + } + else + { + return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount(); + } +} + + +// Called by scrollbar +void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar ) +{ + mScrollLines = new_pos; +} + + +void LLScrollListCtrl::sortByColumn(const std::string& name, bool ascending) +{ + column_map_t::iterator itor = mColumns.find(name); + if (itor != mColumns.end()) + { + sortByColumnIndex((*itor).second->mIndex, ascending); + } +} + +// First column is column 0 +void LLScrollListCtrl::sortByColumnIndex(U32 column, bool ascending) +{ + setSort(column, ascending); + updateSort(); +} + +void LLScrollListCtrl::updateSort() const +{ + if (hasSortOrder() && !isSorted()) + { + // do stable sort to preserve any previous sorts + std::stable_sort( + mItemList.begin(), + mItemList.end(), + SortScrollListItem(mSortColumns,mSortCallback, mAlternateSort)); + + mSorted = true; + } +} + +// for one-shot sorts, does not save sort column/order +void LLScrollListCtrl::sortOnce(S32 column, bool ascending) +{ + std::vector > sort_column; + sort_column.push_back(std::make_pair(column, ascending)); + + // do stable sort to preserve any previous sorts + std::stable_sort( + mItemList.begin(), + mItemList.end(), + SortScrollListItem(sort_column,mSortCallback,mAlternateSort)); +} + +void LLScrollListCtrl::dirtyColumns() +{ + mColumnsDirty = true; + mColumnWidthsDirty = true; + + // need to keep mColumnsIndexed up to date + // just in case someone indexes into it immediately + mColumnsIndexed.resize(mColumns.size()); + + column_map_t::iterator column_itor; + for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) + { + LLScrollListColumn *column = column_itor->second; + mColumnsIndexed[column_itor->second->mIndex] = column; + } +} + + +S32 LLScrollListCtrl::getScrollPos() const +{ + return mScrollbar->getDocPos(); +} + + +void LLScrollListCtrl::setScrollPos( S32 pos ) +{ + mScrollbar->setDocPos( pos ); + + onScrollChange(mScrollbar->getDocPos(), mScrollbar); +} + + +void LLScrollListCtrl::scrollToShowSelected() +{ + // don't scroll automatically when capturing mouse input + // as that will change what is currently under the mouse cursor + if (hasMouseCapture()) + { + return; + } + + updateSort(); + + S32 index = getFirstSelectedIndex(); + if (index < 0) + { + return; + } + + LLScrollListItem* item = mItemList[index]; + if (!item) + { + // I don't THINK this should ever happen. + return; + } + + S32 lowest = mScrollLines; + S32 page_lines = getLinesPerPage(); + S32 highest = mScrollLines + page_lines; + + if (index < lowest) + { + // need to scroll to show item + setScrollPos(index); + } + else if (highest <= index) + { + setScrollPos(index - page_lines + 1); + } +} + +void LLScrollListCtrl::updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width) +{ + mTotalStaticColumnWidth += llmax(0, new_width) - llmax(0, col->getWidth()); +} + +// LLEditMenuHandler functions + +// virtual +void LLScrollListCtrl::copy() +{ + std::string buffer; + + std::vector items = getAllSelected(); + std::vector::iterator itor; + for (itor = items.begin(); itor != items.end(); ++itor) + { + buffer += (*itor)->getContentsCSV() + "\n"; + } + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length()); +} + +// virtual +bool LLScrollListCtrl::canCopy() const +{ + return (getFirstSelected() != NULL); +} + +// virtual +void LLScrollListCtrl::cut() +{ + copy(); + doDelete(); +} + +// virtual +bool LLScrollListCtrl::canCut() const +{ + return canCopy() && canDoDelete(); +} + +// virtual +void LLScrollListCtrl::selectAll() +{ + // Deselects all other items + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + if( itemp->getEnabled() ) + { + selectItem(itemp, -1, false); + } + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } +} + +// virtual +bool LLScrollListCtrl::canSelectAll() const +{ + return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable); +} + +// virtual +void LLScrollListCtrl::deselect() +{ + deselectAllItems(); +} + +// virtual +bool LLScrollListCtrl::canDeselect() const +{ + return getCanSelect(); +} + +void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) +{ + LLScrollListColumn::Params p; + LLParamSDParser parser; + parser.readSD(column, p); + addColumn(p, pos); +} + +void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params, EAddPosition pos) +{ + if (!column_params.validateBlock()) return; + + std::string name = column_params.name; + // if no column name provided, just use ordinal as name + if (name.empty()) + { + name = llformat("%d", mColumnsIndexed.size()); + } + + if (mColumns.find(name) == mColumns.end()) + { + // Add column + mColumns[name] = new LLScrollListColumn(column_params, this); + LLScrollListColumn* new_column = mColumns[name]; + new_column->mIndex = mColumns.size()-1; + + // Add button + if (new_column->getWidth() > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth) + { + if (getNumColumns() > 0) + { + mTotalColumnPadding += mColumnPadding; + } + if (new_column->mRelWidth >= 0) + { + new_column->setWidth((S32)ll_round(new_column->mRelWidth*mItemListRect.getWidth())); + } + else if(new_column->mDynamicWidth) + { + mNumDynamicWidthColumns++; + new_column->setWidth((mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns); + } + S32 top = mItemListRect.mTop; + + S32 left = mItemListRect.mLeft; + for (column_map_t::iterator itor = mColumns.begin(); + itor != mColumns.end(); + ++itor) + { + if (itor->second->mIndex < new_column->mIndex && + itor->second->getWidth() > 0) + { + left += itor->second->getWidth() + mColumnPadding; + } + } + + S32 right = left+new_column->getWidth(); + if (new_column->mIndex != (S32)mColumns.size()-1) + { + right += mColumnPadding; + } + + LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); + + LLScrollColumnHeader::Params params(LLUICtrlFactory::getDefaultParams()); + params.name = "btn_" + name; + params.rect = temp_rect; + params.column = new_column; + params.tool_tip = column_params.tool_tip; + params.tab_stop = false; + params.visible = mDisplayColumnHeaders; + + if(column_params.header.image.isProvided()) + { + params.image_selected = column_params.header.image; + params.image_unselected = column_params.header.image; + } + else + { + params.label = column_params.header.label; + } + + new_column->mHeader = LLUICtrlFactory::create(params); + addChild(new_column->mHeader); + + sendChildToFront(mScrollbar); + } + } + + dirtyColumns(); +} + +// static +void LLScrollListCtrl::onClickColumn(void *userdata) +{ + LLScrollListColumn *info = (LLScrollListColumn*)userdata; + if (!info) return; + + LLScrollListCtrl *parent = info->mParentCtrl; + if (!parent) return; + + if (!parent->mCanSort) return; + + S32 column_index = info->mIndex; + + LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; + bool ascending = column->mSortDirection == LLScrollListColumn::ASCENDING; + if (column->mSortingColumn != column->mName + && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) + { + LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn]; + column_index = info_redir->mIndex; + } + + // if this column is the primary sort key, reverse the direction + if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index) + { + ascending = !parent->mSortColumns.back().second; + } + + parent->sortByColumnIndex(column_index, ascending); + + if (parent->mOnSortChangedCallback) + { + parent->mOnSortChangedCallback(); + } +} + +std::string LLScrollListCtrl::getSortColumnName() +{ + LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first]; + + if (column) return column->mName; + else return ""; +} + +bool LLScrollListCtrl::hasSortOrder() const +{ + return !mSortColumns.empty(); +} + +void LLScrollListCtrl::clearSortOrder() +{ + mSortColumns.clear(); +} + +void LLScrollListCtrl::clearColumns() +{ + column_map_t::iterator itor; + for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) + { + LLScrollColumnHeader *header = itor->second->mHeader; + if (header) + { + removeChild(header); + delete header; + } + } + std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer()); + mColumns.clear(); + mSortColumns.clear(); + mTotalStaticColumnWidth = 0; + mTotalColumnPadding = 0; + + dirtyColumns(); // Clears mColumnsIndexed +} + +void LLScrollListCtrl::setColumnLabel(const std::string& column, const std::string& label) +{ + LLScrollListColumn* columnp = getColumn(column); + if (columnp) + { + columnp->mLabel = label; + if (columnp->mHeader) + { + columnp->mHeader->setLabel(label); + } + } +} + +LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index) +{ + if (index < 0 || index >= (S32)mColumnsIndexed.size()) + { + return NULL; + } + return mColumnsIndexed[index]; +} + +LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name) +{ + column_map_t::iterator column_itor = mColumns.find(name); + if (column_itor != mColumns.end()) + { + return column_itor->second; + } + return NULL; +} + +LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + LLScrollListItem::Params item_params; + LLParamSDParser parser; + parser.readSD(element, item_params); + item_params.userdata = userdata; + return addRow(item_params, pos); +} + +LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_p, EAddPosition pos) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + LLScrollListItem *new_item = new LLScrollListItem(item_p); + return addRow(new_item, item_p, pos); +} + +LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& item_p, EAddPosition pos) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + if (!item_p.validateBlock() || !new_item) return NULL; + new_item->setNumColumns(mColumns.size()); + + // Add any columns we don't already have + S32 col_index = 0; + + for(LLInitParam::ParamIterator::const_iterator itor = item_p.columns.begin(); + itor != item_p.columns.end(); + ++itor) + { + LLScrollListCell::Params cell_p = *itor; + std::string column = cell_p.column; + + // empty columns strings index by ordinal + if (column.empty()) + { + column = llformat("%d", col_index); + } + + LLScrollListColumn* columnp = getColumn(column); + + // create new column on demand + if (!columnp) + { + LLScrollListColumn::Params new_column; + new_column.name = column; + new_column.header.label = column; + + // if width supplied for column, use it, otherwise + // use adaptive width + if (cell_p.width.isProvided()) + { + new_column.width.pixel_width = cell_p.width; + } + addColumn(new_column); + columnp = mColumns[column]; + new_item->setNumColumns(mColumns.size()); + } + + S32 index = columnp->mIndex; + if (!cell_p.width.isProvided()) + { + cell_p.width = columnp->getWidth(); + } + + LLScrollListCell* cell = LLScrollListCell::create(cell_p); + + if (cell) + { + new_item->setColumn(index, cell); + if (columnp->mHeader + && cell->isText() + && !cell->getValue().asString().empty()) + { + columnp->mHeader->setHasResizableElement(true); + } + } + + col_index++; + } + + if (item_p.columns.empty()) + { + if (mColumns.empty()) + { + LLScrollListColumn::Params new_column; + new_column.name = "0"; + + addColumn(new_column); + new_item->setNumColumns(mColumns.size()); + } + + LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value)); + if (cell) + { + LLScrollListColumn* columnp = mColumns.begin()->second; + + new_item->setColumn(0, cell); + if (columnp->mHeader + && cell->isText() + && !cell->getValue().asString().empty()) + { + columnp->mHeader->setHasResizableElement(true); + } + } + } + + // add dummy cells for missing columns + for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it) + { + S32 column_idx = column_it->second->mIndex; + if (new_item->getColumn(column_idx) == NULL) + { + LLScrollListColumn* column_ptr = column_it->second; + LLScrollListCell::Params cell_p; + cell_p.width = column_ptr->getWidth(); + + new_item->setColumn(column_idx, new LLScrollListSpacer(cell_p)); + } + } + + addItem(new_item, pos); + return new_item; +} + +LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) +{ + LLSD entry_id = id; + + if (id.isUndefined()) + { + entry_id = value; + } + + LLScrollListItem::Params item_params; + item_params.value(entry_id); + item_params.columns.add() + .value(value) + .font(LLFontGL::getFontEmojiSmall()); + + return addRow(item_params, pos); +} + +void LLScrollListCtrl::setValue(const LLSD& value ) +{ + LLSD::array_const_iterator itor; + for (itor = value.beginArray(); itor != value.endArray(); ++itor) + { + addElement(*itor); + } +} + +LLSD LLScrollListCtrl::getValue() const +{ + LLScrollListItem *item = getFirstSelected(); + if (!item) return LLSD(); + return item->getValue(); +} + +bool LLScrollListCtrl::operateOnSelection(EOperation op) +{ + if (op == OP_DELETE) + { + deleteSelectedItems(); + return true; + } + else if (op == OP_DESELECT) + { + deselectAllItems(); + } + return false; +} + +bool LLScrollListCtrl::operateOnAll(EOperation op) +{ + if (op == OP_DELETE) + { + clearRows(); + return true; + } + else if (op == OP_DESELECT) + { + deselectAllItems(); + } + else if (op == OP_SELECT) + { + selectAll(); + } + return false; +} +//virtual +void LLScrollListCtrl::setFocus(bool b) +{ + // for tabbing into pristine scroll lists (Finder) + if (!getFirstSelected()) + { + selectFirstItem(); + //onCommit(); // SJB: selectFirstItem() will call onCommit() if appropriate + } + LLUICtrl::setFocus(b); +} + + +// virtual +bool LLScrollListCtrl::isDirty() const +{ + bool grubby = mDirty; + if ( !mAllowMultipleSelection ) + { + grubby = (mOriginalSelection != getFirstSelectedIndex()); + } + return grubby; +} + +// Clear dirty state +void LLScrollListCtrl::resetDirty() +{ + mDirty = false; + mOriginalSelection = getFirstSelectedIndex(); +} + + +//virtual +void LLScrollListCtrl::onFocusReceived() +{ + // forget latent selection changes when getting focus + mSelectionChanged = false; + LLUICtrl::onFocusReceived(); +} + +//virtual +void LLScrollListCtrl::onFocusLost() +{ + if (hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + } + + mSearchString.clear(); + + LLUICtrl::onFocusLost(); +} + +boost::signals2::connection LLScrollListCtrl::setIsFriendCallback(const is_friend_signal_t::slot_type& cb) +{ + if (!mIsFriendSignal) + { + mIsFriendSignal = new is_friend_signal_t(); + } + return mIsFriendSignal->connect(cb); +} + +bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str) +{ + if (filter_str == "" || filter_str == " ") + { + clearHighlightedItems(); + return false; + } + + bool res = false; + + setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red)); + + std::string filter_str_lc(filter_str); + LLStringUtil::toLower(filter_str_lc); + + std::vector data = getAllData(); + std::vector::iterator iter = data.begin(); + while (iter != data.end()) + { + LLScrollListCell* cell = (*iter)->getColumn(0); + if (cell) + { + std::string value = cell->getValue().asString(); + LLStringUtil::toLower(value); + if (value.find(filter_str_lc) == std::string::npos) + { + (*iter)->setHighlighted(false); + } + else + { + (*iter)->setHighlighted(true); + res = true; + } + } + iter++; + } + return res; +} diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index d6ba9240b7..356d40ce3c 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -1,564 +1,564 @@ -/** - * @file llscrolllistctrl.h - * @brief A scrolling list of items. This is the one you want to use - * in UI code. LLScrollListCell, LLScrollListItem, etc. are utility - * classes. - * - * $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$ - */ - -#ifndef LL_SCROLLLISTCTRL_H -#define LL_SCROLLLISTCTRL_H - -#include -#include - -#include "lluictrl.h" -#include "llctrlselectioninterface.h" -#include "llfontgl.h" -#include "llui.h" -#include "llstring.h" // LLWString -#include "lleditmenuhandler.h" -#include "llframetimer.h" - -#include "llscrollbar.h" -#include "lldate.h" -#include "llscrolllistitem.h" -#include "llscrolllistcolumn.h" -#include "llviewborder.h" - -class LLScrollListCell; -class LLTextBox; -class LLContextMenu; - -class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler, - public LLCtrlListInterface, public LLCtrlScrollInterface -{ -public: - typedef enum e_selection_type - { - ROW, // default - CELL, // does not support multi-selection - HEADER, // when pointing to cells in column 0 will highlight whole row, otherwise cell, no multi-select - } ESelectionType; - - struct SelectionTypeNames : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - struct Contents : public LLInitParam::Block - { - Multiple columns; - Multiple rows; - - //Multiple groups; - - Contents(); - }; - - // *TODO: Add callbacks to Params - typedef boost::function callback_t; - - template struct maximum - { - typedef T result_type; - - template - T operator()(InputIterator first, InputIterator last) const - { - // If there are no slots to call, just return the - // default-constructed value - if(first == last ) return T(); - T max_value = *first++; - while (first != last) { - if (max_value < *first) - max_value = *first; - ++first; - } - - return max_value; - } - }; - - - typedef boost::signals2::signal > sort_signal_t; - typedef boost::signals2::signal is_friend_signal_t; - - struct Params : public LLInitParam::Block - { - // behavioral flags - Optional multi_select, - commit_on_keyboard_movement, - commit_on_selection_change, - mouse_wheel_opaque; - - Optional selection_type; - - // display flags - Optional has_border, - draw_heading, - draw_stripes, - background_visible, - scroll_bar_bg_visible; - - // layout - Optional column_padding, - row_padding, - page_lines, - heading_height; - - // sort and search behavior - Optional search_column, - sort_column; - Optional sort_ascending, - can_sort; // whether user is allowed to sort - - // colors - Optional fg_unselected_color, - fg_selected_color, - bg_selected_color, - fg_disable_color, - bg_writeable_color, - bg_readonly_color, - bg_stripe_color, - hovered_color, - highlighted_color, - scroll_bar_bg_color; - - Optional contents; - - Optional border; - - Params(); - }; - -protected: - friend class LLUICtrlFactory; - - LLScrollListCtrl(const Params&); - -public: - virtual ~LLScrollListCtrl(); - - S32 isEmpty() const; - - void deleteAllItems() { clearRows(); } - - // Sets an array of column descriptors - void setColumnHeadings(const LLSD& headings); - void sortByColumnIndex(U32 column, bool ascending); - - // LLCtrlListInterface functions - virtual S32 getItemCount() const; - // Adds a single column descriptor: ["name" : string, "label" : string, "width" : integer, "relwidth" : integer ] - virtual void addColumn(const LLScrollListColumn::Params& column, EAddPosition pos = ADD_BOTTOM); - 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 bool preProcessChildNode(LLXMLNodePtr child); - virtual LLScrollListColumn* getColumn(S32 index); - virtual LLScrollListColumn* getColumn(const std::string& name); - virtual S32 getNumColumns() const { return mColumnsIndexed.size(); } - - // Adds a single element, from an array of: - // "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid - // Creates missing columns automatically. - virtual LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); - virtual LLScrollListItem* addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM); - virtual LLScrollListItem* addRow(const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM); - // Simple add element. Takes a single array of: - // [ "value" => value, "font" => font, "font-style" => style ] - virtual void clearRows(); // clears all elements - virtual void sortByColumn(const std::string& name, bool ascending); - - // These functions take and return an array of arrays of elements, as above - virtual void setValue(const LLSD& value ); - virtual LLSD getValue() const; - - LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; } - LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; } - LLCtrlScrollInterface* getScrollInterface() { return (LLCtrlScrollInterface*)this; } - - // DEPRECATED: Use setSelectedByValue() below. - bool setCurrentByID( const LLUUID& id ) { return selectByID(id); } - virtual LLUUID getCurrentID() const { return getStringUUIDSelectedItem(); } - - bool operateOnSelection(EOperation op); - bool operateOnAll(EOperation op); - - // returns false if unable to set the max count so low - bool setMaxItemCount(S32 max_count); - - bool selectByID( const LLUUID& id ); // false if item not found - - // Match item by value.asString(), which should work for string, integer, uuid. - // Returns false if not found. - bool setSelectedByValue(const LLSD& value, bool selected); - - bool isSorted() const { return mSorted; } - - virtual bool isSelected(const LLSD& value) const; - - bool hasSelectedItem() const; - - bool handleClick(S32 x, S32 y, MASK mask); - bool selectFirstItem(); - bool selectNthItem( S32 index ); - bool selectItemRange( S32 first, S32 last ); - bool selectItemAt(S32 x, S32 y, MASK mask); - - void deleteSingleItem( S32 index ); - void deleteItems(const LLSD& sd); - void deleteSelectedItems(); - void deselectAllItems(bool no_commit_on_change = false); // by default, go ahead and commit on selection change - - void clearHighlightedItems(); - - virtual void mouseOverHighlightNthItem( S32 index ); - - S32 getHighlightedItemInx() const { return mHighlightedItem; } - - void setDoubleClickCallback( callback_t cb ) { mOnDoubleClickCallback = cb; } - void setMaximumSelectCallback( callback_t cb) { mOnMaximumSelectCallback = cb; } - void setSortChangedCallback( callback_t cb) { mOnSortChangedCallback = cb; } - // Convenience function; *TODO: replace with setter above + boost::bind() in calling code - void setDoubleClickCallback( boost::function cb, void* userdata) { mOnDoubleClickCallback = boost::bind(cb, userdata); } - - void swapWithNext(S32 index); - void swapWithPrevious(S32 index); - - void setCanSelect(bool can_select) { mCanSelect = can_select; } - virtual bool getCanSelect() const { return mCanSelect; } - - S32 getItemIndex( LLScrollListItem* item ) const; - S32 getItemIndex( const LLUUID& item_id ) const; - - void setCommentText( const std::string& comment_text); - LLScrollListItem* addSeparator(EAddPosition pos); - - // "Simple" interface: use this when you're creating a list that contains only unique strings, only - // one of which can be selected at a time. - virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD()); - - bool selectItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 ); // false if item not found - bool selectItemByPrefix(const std::string& target, bool case_sensitive = true, S32 column = -1); - bool selectItemByPrefix(const LLWString& target, bool case_sensitive = true, S32 column = -1); - LLScrollListItem* getItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 ); - const std::string getSelectedItemLabel(S32 column = 0) const; - LLSD getSelectedValue(); - - // If multi select is on, select all element that include substring, - // otherwise select first match only. - // If focus is true will scroll to selection. - // Returns number of results. - // Note: at the moment search happens in one go and is expensive - U32 searchItems(const std::string& substring, bool case_sensitive = false, bool focus = true); - U32 searchItems(const LLWString& substring, bool case_sensitive = false, bool focus = true); - - // DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue(). - // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which - // has an associated, unique UUID, and only one of which can be selected at a time. - LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true); - LLUUID getStringUUIDSelectedItem() const; - - LLScrollListItem* getFirstSelected() const; - virtual S32 getFirstSelectedIndex() const; - std::vector getAllSelected() const; - S32 getNumSelected() const; - LLScrollListItem* getLastSelectedItem() const { return mLastSelected; } - - // iterate over all items - LLScrollListItem* getFirstData() const; - LLScrollListItem* getLastData() const; - std::vector getAllData() const; - - LLScrollListItem* getItem(const LLSD& sd) const; - - void setAllowMultipleSelection(bool mult ) { mAllowMultipleSelection = mult; } - - void setBgWriteableColor(const LLColor4 &c) { mBgWriteableColor = c; } - void setReadOnlyBgColor(const LLColor4 &c) { mBgReadOnlyColor = c; } - void setBgSelectedColor(const LLColor4 &c) { mBgSelectedColor = c; } - void setBgStripeColor(const LLColor4& c) { mBgStripeColor = c; } - void setFgSelectedColor(const LLColor4 &c) { mFgSelectedColor = c; } - void setFgUnselectedColor(const LLColor4 &c){ mFgUnselectedColor = c; } - void setHoveredColor(const LLColor4 &c) { mHoveredColor = c; } - void setHighlightedColor(const LLColor4 &c) { mHighlightedColor = c; } - void setFgDisableColor(const LLColor4 &c) { mFgDisabledColor = c; } - - void setBackgroundVisible(bool b) { mBackgroundVisible = b; } - void setDrawStripes(bool b) { mDrawStripes = b; } - void setColumnPadding(const S32 c) { mColumnPadding = c; } - S32 getColumnPadding() const { return mColumnPadding; } - void setRowPadding(const S32 c) { mColumnPadding = c; } - S32 getRowPadding() const { return mColumnPadding; } - void setCommitOnKeyboardMovement(bool b) { mCommitOnKeyboardMovement = b; } - void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; } - void setAllowKeyboardMovement(bool b) { mAllowKeyboardMovement = b; } - - void setMaxSelectable(U32 max_selected) { mMaxSelectable = max_selected; } - S32 getMaxSelectable() { return mMaxSelectable; } - - - virtual S32 getScrollPos() const; - virtual void setScrollPos( S32 pos ); - S32 getSearchColumn(); - void setSearchColumn(S32 column) { mSearchColumn = column; } - S32 getColumnIndexFromOffset(S32 x); - S32 getColumnOffsetFromIndex(S32 index); - S32 getRowOffsetFromIndex(S32 index); - - void clearSearchString() { mSearchString.clear(); } - - // support right-click context menus for avatar/group lists - enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP }; - void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; } - ContextMenuType getContextMenuType() { return mContextMenuType; } - - // Overridden from LLView - /*virtual*/ void draw(); - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleKeyHere(KEY key, MASK mask); - /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); - /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - /*virtual*/ void setEnabled(bool enabled); - /*virtual*/ void setFocus( bool b ); - /*virtual*/ void onFocusReceived(); - /*virtual*/ void onFocusLost(); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - - virtual bool isDirty() const; - virtual void resetDirty(); // Clear dirty state - - virtual void updateLayout(); - virtual void fitContents(S32 max_width, S32 max_height); - - virtual LLRect getRequiredRect(); - static bool rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row); - - LLRect getItemListRect() { return mItemListRect; } - - /// Returns rect, in local coords, of a given row/column - LLRect getCellRect(S32 row_index, S32 column_index); - - // Used "internally" by the scroll bar. - void onScrollChange( S32 new_pos, LLScrollbar* src ); - - static void onClickColumn(void *userdata); - - virtual void updateColumns(bool force_update = false); - S32 calcMaxContentWidth(); - bool updateColumnWidths(); - - void setHeadingHeight(S32 heading_height); - /** - * Sets max visible lines without scroolbar, if this value equals to 0, - * then display all items. - */ - void setPageLines(S32 page_lines ); - void setCollapseEmptyColumns(bool collapse); - - LLScrollListItem* hitItem(S32 x,S32 y); - virtual void scrollToShowSelected(); - - // LLEditMenuHandler functions - virtual void copy(); - virtual bool canCopy() const; - virtual void cut(); - virtual bool canCut() const; - virtual void selectAll(); - virtual bool canSelectAll() const; - virtual void deselect(); - virtual bool canDeselect() const; - - void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; } - void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width); - S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; } - - std::string getSortColumnName(); - bool getSortAscending() { return mSortColumns.empty() ? true : mSortColumns.back().second; } - bool hasSortOrder() const; - void clearSortOrder(); - - void setAlternateSort() { mAlternateSort = true; } - - void selectPrevItem(bool extend_selection = false); - void selectNextItem(bool extend_selection = false); - S32 selectMultiple(uuid_vec_t ids); - // conceptually const, but mutates mItemList - void updateSort() const; - // sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example) - void sortOnce(S32 column, bool ascending); - - // manually call this whenever editing list items in place to flag need for resorting - void setNeedsSort(bool val = true) { mSorted = !val; } - void dirtyColumns(); // some operation has potentially affected column layout or ordering - - bool highlightMatchingItems(const std::string& filter_str); - - boost::signals2::connection setSortCallback(sort_signal_t::slot_type cb ) - { - if (!mSortCallback) mSortCallback = new sort_signal_t(); - return mSortCallback->connect(cb); - } - - boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb); - - -protected: - // "Full" interface: use this when you're creating a list that has one or more of the following: - // * contains icons - // * contains multiple columns - // * allows multiple selection - // * has items that are not guarenteed to have unique names - // * has additional per-item data (e.g. a UUID or void* userdata) - // - // To add items using this approach, create new LLScrollListItems and LLScrollListCells. Add the - // cells (column entries) to each item, and add the item to the LLScrollListCtrl. - // - // The LLScrollListCtrl owns its items and is responsible for deleting them - // (except in the case that the addItem() call fails, in which case it is up - // to the caller to delete the item) - // - // returns false if item faile to be added to list, does NOT delete 'item' - bool addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM, bool requires_column = true ); - - typedef std::deque item_list; - item_list& getItemList() { return mItemList; } - - void updateLineHeight(); - -private: - void drawItems(); - - void updateLineHeightInsert(LLScrollListItem* item); - void reportInvalidInput(); - bool isRepeatedChars(const LLWString& string) const; - void selectItem(LLScrollListItem* itemp, S32 cell, bool single_select = true); - void deselectItem(LLScrollListItem* itemp); - void commitIfChanged(); - bool setSort(S32 column, bool ascending); - S32 getLinesPerPage(); - - static void showProfile(std::string id, bool is_group); - static void sendIM(std::string id); - static void addFriend(std::string id); - static void removeFriend(std::string id); - static void reportAbuse(std::string id, bool is_group); - static void showNameDetails(std::string id, bool is_group); - static void copyNameToClipboard(std::string id, bool is_group); - static void copySLURLToClipboard(std::string id, bool is_group); - - S32 mLineHeight; // the max height of a single line - S32 mScrollLines; // how many lines we've scrolled down - S32 mPageLines; // max number of lines is it possible to see on the screen given mRect and mLineHeight - S32 mHeadingHeight; // the height of the column header buttons, if visible - U32 mMaxSelectable; - LLScrollbar* mScrollbar; - bool mAllowMultipleSelection; - bool mAllowKeyboardMovement; - bool mCommitOnKeyboardMovement; - bool mCommitOnSelectionChange; - bool mSelectionChanged; - ESelectionType mSelectionType; - bool mNeedsScroll; - bool mMouseWheelOpaque; - bool mCanSelect; - bool mCanSort; // Whether user is allowed to sort - bool mDisplayColumnHeaders; - bool mColumnsDirty; - bool mColumnWidthsDirty; - - bool mAlternateSort; - - mutable item_list mItemList; - - LLScrollListItem *mLastSelected; - - S32 mMaxItemCount; - - LLRect mItemListRect; - S32 mColumnPadding; - S32 mRowPadding; - - bool mBackgroundVisible; - bool mDrawStripes; - - LLUIColor mBgWriteableColor; - LLUIColor mBgReadOnlyColor; - LLUIColor mBgSelectedColor; - LLUIColor mBgStripeColor; - LLUIColor mFgSelectedColor; - LLUIColor mFgUnselectedColor; - LLUIColor mFgDisabledColor; - LLUIColor mHoveredColor; - LLUIColor mHighlightedColor; - - S32 mBorderThickness; - callback_t mOnDoubleClickCallback; - callback_t mOnMaximumSelectCallback; - callback_t mOnSortChangedCallback; - - S32 mHighlightedItem; - class LLViewBorder* mBorder; - LLHandle mPopupMenuHandle; - - LLView *mCommentTextView; - - LLWString mSearchString; - LLFrameTimer mSearchTimer; - - S32 mSearchColumn; - S32 mNumDynamicWidthColumns; - S32 mTotalStaticColumnWidth; - S32 mTotalColumnPadding; - - mutable bool mSorted; - - typedef std::map column_map_t; - column_map_t mColumns; - - bool mDirty; - S32 mOriginalSelection; - - ContextMenuType mContextMenuType; - - typedef std::vector ordered_columns_t; - ordered_columns_t mColumnsIndexed; - - typedef std::pair sort_column_t; - std::vector mSortColumns; - - sort_signal_t* mSortCallback; - - is_friend_signal_t* mIsFriendSignal; -}; // end class LLScrollListCtrl - -#endif // LL_SCROLLLISTCTRL_H +/** + * @file llscrolllistctrl.h + * @brief A scrolling list of items. This is the one you want to use + * in UI code. LLScrollListCell, LLScrollListItem, etc. are utility + * classes. + * + * $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$ + */ + +#ifndef LL_SCROLLLISTCTRL_H +#define LL_SCROLLLISTCTRL_H + +#include +#include + +#include "lluictrl.h" +#include "llctrlselectioninterface.h" +#include "llfontgl.h" +#include "llui.h" +#include "llstring.h" // LLWString +#include "lleditmenuhandler.h" +#include "llframetimer.h" + +#include "llscrollbar.h" +#include "lldate.h" +#include "llscrolllistitem.h" +#include "llscrolllistcolumn.h" +#include "llviewborder.h" + +class LLScrollListCell; +class LLTextBox; +class LLContextMenu; + +class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler, + public LLCtrlListInterface, public LLCtrlScrollInterface +{ +public: + typedef enum e_selection_type + { + ROW, // default + CELL, // does not support multi-selection + HEADER, // when pointing to cells in column 0 will highlight whole row, otherwise cell, no multi-select + } ESelectionType; + + struct SelectionTypeNames : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + struct Contents : public LLInitParam::Block + { + Multiple columns; + Multiple rows; + + //Multiple groups; + + Contents(); + }; + + // *TODO: Add callbacks to Params + typedef boost::function callback_t; + + template struct maximum + { + typedef T result_type; + + template + T operator()(InputIterator first, InputIterator last) const + { + // If there are no slots to call, just return the + // default-constructed value + if(first == last ) return T(); + T max_value = *first++; + while (first != last) { + if (max_value < *first) + max_value = *first; + ++first; + } + + return max_value; + } + }; + + + typedef boost::signals2::signal > sort_signal_t; + typedef boost::signals2::signal is_friend_signal_t; + + struct Params : public LLInitParam::Block + { + // behavioral flags + Optional multi_select, + commit_on_keyboard_movement, + commit_on_selection_change, + mouse_wheel_opaque; + + Optional selection_type; + + // display flags + Optional has_border, + draw_heading, + draw_stripes, + background_visible, + scroll_bar_bg_visible; + + // layout + Optional column_padding, + row_padding, + page_lines, + heading_height; + + // sort and search behavior + Optional search_column, + sort_column; + Optional sort_ascending, + can_sort; // whether user is allowed to sort + + // colors + Optional fg_unselected_color, + fg_selected_color, + bg_selected_color, + fg_disable_color, + bg_writeable_color, + bg_readonly_color, + bg_stripe_color, + hovered_color, + highlighted_color, + scroll_bar_bg_color; + + Optional contents; + + Optional border; + + Params(); + }; + +protected: + friend class LLUICtrlFactory; + + LLScrollListCtrl(const Params&); + +public: + virtual ~LLScrollListCtrl(); + + S32 isEmpty() const; + + void deleteAllItems() { clearRows(); } + + // Sets an array of column descriptors + void setColumnHeadings(const LLSD& headings); + void sortByColumnIndex(U32 column, bool ascending); + + // LLCtrlListInterface functions + virtual S32 getItemCount() const; + // Adds a single column descriptor: ["name" : string, "label" : string, "width" : integer, "relwidth" : integer ] + virtual void addColumn(const LLScrollListColumn::Params& column, EAddPosition pos = ADD_BOTTOM); + 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 bool preProcessChildNode(LLXMLNodePtr child); + virtual LLScrollListColumn* getColumn(S32 index); + virtual LLScrollListColumn* getColumn(const std::string& name); + virtual S32 getNumColumns() const { return mColumnsIndexed.size(); } + + // Adds a single element, from an array of: + // "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid + // Creates missing columns automatically. + virtual LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); + virtual LLScrollListItem* addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM); + virtual LLScrollListItem* addRow(const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM); + // Simple add element. Takes a single array of: + // [ "value" => value, "font" => font, "font-style" => style ] + virtual void clearRows(); // clears all elements + virtual void sortByColumn(const std::string& name, bool ascending); + + // These functions take and return an array of arrays of elements, as above + virtual void setValue(const LLSD& value ); + virtual LLSD getValue() const; + + LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; } + LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; } + LLCtrlScrollInterface* getScrollInterface() { return (LLCtrlScrollInterface*)this; } + + // DEPRECATED: Use setSelectedByValue() below. + bool setCurrentByID( const LLUUID& id ) { return selectByID(id); } + virtual LLUUID getCurrentID() const { return getStringUUIDSelectedItem(); } + + bool operateOnSelection(EOperation op); + bool operateOnAll(EOperation op); + + // returns false if unable to set the max count so low + bool setMaxItemCount(S32 max_count); + + bool selectByID( const LLUUID& id ); // false if item not found + + // Match item by value.asString(), which should work for string, integer, uuid. + // Returns false if not found. + bool setSelectedByValue(const LLSD& value, bool selected); + + bool isSorted() const { return mSorted; } + + virtual bool isSelected(const LLSD& value) const; + + bool hasSelectedItem() const; + + bool handleClick(S32 x, S32 y, MASK mask); + bool selectFirstItem(); + bool selectNthItem( S32 index ); + bool selectItemRange( S32 first, S32 last ); + bool selectItemAt(S32 x, S32 y, MASK mask); + + void deleteSingleItem( S32 index ); + void deleteItems(const LLSD& sd); + void deleteSelectedItems(); + void deselectAllItems(bool no_commit_on_change = false); // by default, go ahead and commit on selection change + + void clearHighlightedItems(); + + virtual void mouseOverHighlightNthItem( S32 index ); + + S32 getHighlightedItemInx() const { return mHighlightedItem; } + + void setDoubleClickCallback( callback_t cb ) { mOnDoubleClickCallback = cb; } + void setMaximumSelectCallback( callback_t cb) { mOnMaximumSelectCallback = cb; } + void setSortChangedCallback( callback_t cb) { mOnSortChangedCallback = cb; } + // Convenience function; *TODO: replace with setter above + boost::bind() in calling code + void setDoubleClickCallback( boost::function cb, void* userdata) { mOnDoubleClickCallback = boost::bind(cb, userdata); } + + void swapWithNext(S32 index); + void swapWithPrevious(S32 index); + + void setCanSelect(bool can_select) { mCanSelect = can_select; } + virtual bool getCanSelect() const { return mCanSelect; } + + S32 getItemIndex( LLScrollListItem* item ) const; + S32 getItemIndex( const LLUUID& item_id ) const; + + void setCommentText( const std::string& comment_text); + LLScrollListItem* addSeparator(EAddPosition pos); + + // "Simple" interface: use this when you're creating a list that contains only unique strings, only + // one of which can be selected at a time. + virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD()); + + bool selectItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 ); // false if item not found + bool selectItemByPrefix(const std::string& target, bool case_sensitive = true, S32 column = -1); + bool selectItemByPrefix(const LLWString& target, bool case_sensitive = true, S32 column = -1); + LLScrollListItem* getItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 ); + const std::string getSelectedItemLabel(S32 column = 0) const; + LLSD getSelectedValue(); + + // If multi select is on, select all element that include substring, + // otherwise select first match only. + // If focus is true will scroll to selection. + // Returns number of results. + // Note: at the moment search happens in one go and is expensive + U32 searchItems(const std::string& substring, bool case_sensitive = false, bool focus = true); + U32 searchItems(const LLWString& substring, bool case_sensitive = false, bool focus = true); + + // DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue(). + // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which + // has an associated, unique UUID, and only one of which can be selected at a time. + LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true); + LLUUID getStringUUIDSelectedItem() const; + + LLScrollListItem* getFirstSelected() const; + virtual S32 getFirstSelectedIndex() const; + std::vector getAllSelected() const; + S32 getNumSelected() const; + LLScrollListItem* getLastSelectedItem() const { return mLastSelected; } + + // iterate over all items + LLScrollListItem* getFirstData() const; + LLScrollListItem* getLastData() const; + std::vector getAllData() const; + + LLScrollListItem* getItem(const LLSD& sd) const; + + void setAllowMultipleSelection(bool mult ) { mAllowMultipleSelection = mult; } + + void setBgWriteableColor(const LLColor4 &c) { mBgWriteableColor = c; } + void setReadOnlyBgColor(const LLColor4 &c) { mBgReadOnlyColor = c; } + void setBgSelectedColor(const LLColor4 &c) { mBgSelectedColor = c; } + void setBgStripeColor(const LLColor4& c) { mBgStripeColor = c; } + void setFgSelectedColor(const LLColor4 &c) { mFgSelectedColor = c; } + void setFgUnselectedColor(const LLColor4 &c){ mFgUnselectedColor = c; } + void setHoveredColor(const LLColor4 &c) { mHoveredColor = c; } + void setHighlightedColor(const LLColor4 &c) { mHighlightedColor = c; } + void setFgDisableColor(const LLColor4 &c) { mFgDisabledColor = c; } + + void setBackgroundVisible(bool b) { mBackgroundVisible = b; } + void setDrawStripes(bool b) { mDrawStripes = b; } + void setColumnPadding(const S32 c) { mColumnPadding = c; } + S32 getColumnPadding() const { return mColumnPadding; } + void setRowPadding(const S32 c) { mColumnPadding = c; } + S32 getRowPadding() const { return mColumnPadding; } + void setCommitOnKeyboardMovement(bool b) { mCommitOnKeyboardMovement = b; } + void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; } + void setAllowKeyboardMovement(bool b) { mAllowKeyboardMovement = b; } + + void setMaxSelectable(U32 max_selected) { mMaxSelectable = max_selected; } + S32 getMaxSelectable() { return mMaxSelectable; } + + + virtual S32 getScrollPos() const; + virtual void setScrollPos( S32 pos ); + S32 getSearchColumn(); + void setSearchColumn(S32 column) { mSearchColumn = column; } + S32 getColumnIndexFromOffset(S32 x); + S32 getColumnOffsetFromIndex(S32 index); + S32 getRowOffsetFromIndex(S32 index); + + void clearSearchString() { mSearchString.clear(); } + + // support right-click context menus for avatar/group lists + enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP }; + void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; } + ContextMenuType getContextMenuType() { return mContextMenuType; } + + // Overridden from LLView + /*virtual*/ void draw(); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask); + /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char); + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ void setEnabled(bool enabled); + /*virtual*/ void setFocus( bool b ); + /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost(); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + + virtual bool isDirty() const; + virtual void resetDirty(); // Clear dirty state + + virtual void updateLayout(); + virtual void fitContents(S32 max_width, S32 max_height); + + virtual LLRect getRequiredRect(); + static bool rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row); + + LLRect getItemListRect() { return mItemListRect; } + + /// Returns rect, in local coords, of a given row/column + LLRect getCellRect(S32 row_index, S32 column_index); + + // Used "internally" by the scroll bar. + void onScrollChange( S32 new_pos, LLScrollbar* src ); + + static void onClickColumn(void *userdata); + + virtual void updateColumns(bool force_update = false); + S32 calcMaxContentWidth(); + bool updateColumnWidths(); + + void setHeadingHeight(S32 heading_height); + /** + * Sets max visible lines without scroolbar, if this value equals to 0, + * then display all items. + */ + void setPageLines(S32 page_lines ); + void setCollapseEmptyColumns(bool collapse); + + LLScrollListItem* hitItem(S32 x,S32 y); + virtual void scrollToShowSelected(); + + // LLEditMenuHandler functions + virtual void copy(); + virtual bool canCopy() const; + virtual void cut(); + virtual bool canCut() const; + virtual void selectAll(); + virtual bool canSelectAll() const; + virtual void deselect(); + virtual bool canDeselect() const; + + void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; } + void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width); + S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; } + + std::string getSortColumnName(); + bool getSortAscending() { return mSortColumns.empty() ? true : mSortColumns.back().second; } + bool hasSortOrder() const; + void clearSortOrder(); + + void setAlternateSort() { mAlternateSort = true; } + + void selectPrevItem(bool extend_selection = false); + void selectNextItem(bool extend_selection = false); + S32 selectMultiple(uuid_vec_t ids); + // conceptually const, but mutates mItemList + void updateSort() const; + // sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example) + void sortOnce(S32 column, bool ascending); + + // manually call this whenever editing list items in place to flag need for resorting + void setNeedsSort(bool val = true) { mSorted = !val; } + void dirtyColumns(); // some operation has potentially affected column layout or ordering + + bool highlightMatchingItems(const std::string& filter_str); + + boost::signals2::connection setSortCallback(sort_signal_t::slot_type cb ) + { + if (!mSortCallback) mSortCallback = new sort_signal_t(); + return mSortCallback->connect(cb); + } + + boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb); + + +protected: + // "Full" interface: use this when you're creating a list that has one or more of the following: + // * contains icons + // * contains multiple columns + // * allows multiple selection + // * has items that are not guarenteed to have unique names + // * has additional per-item data (e.g. a UUID or void* userdata) + // + // To add items using this approach, create new LLScrollListItems and LLScrollListCells. Add the + // cells (column entries) to each item, and add the item to the LLScrollListCtrl. + // + // The LLScrollListCtrl owns its items and is responsible for deleting them + // (except in the case that the addItem() call fails, in which case it is up + // to the caller to delete the item) + // + // returns false if item faile to be added to list, does NOT delete 'item' + bool addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM, bool requires_column = true ); + + typedef std::deque item_list; + item_list& getItemList() { return mItemList; } + + void updateLineHeight(); + +private: + void drawItems(); + + void updateLineHeightInsert(LLScrollListItem* item); + void reportInvalidInput(); + bool isRepeatedChars(const LLWString& string) const; + void selectItem(LLScrollListItem* itemp, S32 cell, bool single_select = true); + void deselectItem(LLScrollListItem* itemp); + void commitIfChanged(); + bool setSort(S32 column, bool ascending); + S32 getLinesPerPage(); + + static void showProfile(std::string id, bool is_group); + static void sendIM(std::string id); + static void addFriend(std::string id); + static void removeFriend(std::string id); + static void reportAbuse(std::string id, bool is_group); + static void showNameDetails(std::string id, bool is_group); + static void copyNameToClipboard(std::string id, bool is_group); + static void copySLURLToClipboard(std::string id, bool is_group); + + S32 mLineHeight; // the max height of a single line + S32 mScrollLines; // how many lines we've scrolled down + S32 mPageLines; // max number of lines is it possible to see on the screen given mRect and mLineHeight + S32 mHeadingHeight; // the height of the column header buttons, if visible + U32 mMaxSelectable; + LLScrollbar* mScrollbar; + bool mAllowMultipleSelection; + bool mAllowKeyboardMovement; + bool mCommitOnKeyboardMovement; + bool mCommitOnSelectionChange; + bool mSelectionChanged; + ESelectionType mSelectionType; + bool mNeedsScroll; + bool mMouseWheelOpaque; + bool mCanSelect; + bool mCanSort; // Whether user is allowed to sort + bool mDisplayColumnHeaders; + bool mColumnsDirty; + bool mColumnWidthsDirty; + + bool mAlternateSort; + + mutable item_list mItemList; + + LLScrollListItem *mLastSelected; + + S32 mMaxItemCount; + + LLRect mItemListRect; + S32 mColumnPadding; + S32 mRowPadding; + + bool mBackgroundVisible; + bool mDrawStripes; + + LLUIColor mBgWriteableColor; + LLUIColor mBgReadOnlyColor; + LLUIColor mBgSelectedColor; + LLUIColor mBgStripeColor; + LLUIColor mFgSelectedColor; + LLUIColor mFgUnselectedColor; + LLUIColor mFgDisabledColor; + LLUIColor mHoveredColor; + LLUIColor mHighlightedColor; + + S32 mBorderThickness; + callback_t mOnDoubleClickCallback; + callback_t mOnMaximumSelectCallback; + callback_t mOnSortChangedCallback; + + S32 mHighlightedItem; + class LLViewBorder* mBorder; + LLHandle mPopupMenuHandle; + + LLView *mCommentTextView; + + LLWString mSearchString; + LLFrameTimer mSearchTimer; + + S32 mSearchColumn; + S32 mNumDynamicWidthColumns; + S32 mTotalStaticColumnWidth; + S32 mTotalColumnPadding; + + mutable bool mSorted; + + typedef std::map column_map_t; + column_map_t mColumns; + + bool mDirty; + S32 mOriginalSelection; + + ContextMenuType mContextMenuType; + + typedef std::vector ordered_columns_t; + ordered_columns_t mColumnsIndexed; + + typedef std::pair sort_column_t; + std::vector mSortColumns; + + sort_signal_t* mSortCallback; + + is_friend_signal_t* mIsFriendSignal; +}; // end class LLScrollListCtrl + +#endif // LL_SCROLLLISTCTRL_H diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp index 8cfe971fa6..85da55e3e6 100644 --- a/indra/llui/llscrolllistitem.cpp +++ b/indra/llui/llscrolllistitem.cpp @@ -1,204 +1,204 @@ -/** - * @file llscrolllistitem.cpp - * @brief Scroll lists are composed of rows (items), each of which - * contains columns (cells). - * - * $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 "linden_common.h" - -#include "llscrolllistitem.h" - -#include "llrect.h" -#include "llui.h" - - -//--------------------------------------------------------------------------- -// LLScrollListItem -//--------------------------------------------------------------------------- - -LLScrollListItem::LLScrollListItem( const Params& p ) -: mSelected(false), - mHighlighted(false), - mHoverIndex(-1), - mSelectedIndex(-1), - mEnabled(p.enabled), - mUserdata(p.userdata), - mItemValue(p.value), - mItemAltValue(p.alt_value) -{ -} - - -LLScrollListItem::~LLScrollListItem() -{ - std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); - mColumns.clear(); -} - -void LLScrollListItem::setSelected(bool b) -{ - mSelected = b; - mSelectedIndex = -1; -} - -void LLScrollListItem::setHighlighted(bool b) -{ - mHighlighted = b; - mHoverIndex = -1; -} - -void LLScrollListItem::setHoverCell(S32 cell) -{ - mHoverIndex = cell; -} - -void LLScrollListItem::setSelectedCell(S32 cell) -{ - mSelectedIndex = cell; -} - -void LLScrollListItem::addColumn(const LLScrollListCell::Params& p) -{ - mColumns.push_back(LLScrollListCell::create(p)); -} - -void LLScrollListItem::setNumColumns(S32 columns) -{ - S32 prev_columns = mColumns.size(); - if (columns < prev_columns) - { - std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer()); - } - - mColumns.resize(columns); - - for (S32 col = prev_columns; col < columns; ++col) - { - mColumns[col] = NULL; - } -} - -void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell ) -{ - if (column < (S32)mColumns.size()) - { - delete mColumns[column]; - mColumns[column] = cell; - } - else - { - LL_ERRS() << "LLScrollListItem::setColumn: bad column: " << column << LL_ENDL; - } -} - - -S32 LLScrollListItem::getNumColumns() const -{ - return mColumns.size(); -} - -LLScrollListCell* LLScrollListItem::getColumn(const S32 i) const -{ - if (0 <= i && i < (S32)mColumns.size()) - { - return mColumns[i]; - } - return NULL; -} - -std::string LLScrollListItem::getContentsCSV() const -{ - std::string ret; - - S32 count = getNumColumns(); - for (S32 i=0; igetValue().asString(); - if (i < count-1) - { - ret += ", "; - } - } - - return ret; -} - - -void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& hover_color, const LLColor4& select_color, const LLColor4& highlight_color, S32 column_padding) -{ - // draw background rect - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLRect bg_rect = rect; - if (mSelectedIndex < 0 && getSelected()) - { - // Whole item is highlighted/selected - gl_rect_2d(bg_rect, select_color); - } - else if (mHoverIndex < 0) - { - // Whole item is highlighted/selected - gl_rect_2d(bg_rect, hover_color); - } - - S32 cur_x = rect.mLeft; - S32 num_cols = getNumColumns(); - S32 cur_col = 0; - - for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols; cell = getColumn(++cur_col)) - { - // Two ways a cell could be hidden - if (cell->getWidth() < 0 - || !cell->getVisible()) continue; - - LLUI::pushMatrix(); - { - LLUI::translate((F32) cur_x, (F32) rect.mBottom); - - if (mSelectedIndex == cur_col) - { - // select specific cell - LLRect highlight_rect(0, - cell->getHeight(), - cell->getWidth(), - 0); - gl_rect_2d(highlight_rect, select_color); - } - else if (mHoverIndex == cur_col) - { - // highlight specific cell - LLRect highlight_rect(0, - cell->getHeight(), - cell->getWidth() , - 0); - gl_rect_2d(highlight_rect, hover_color); - } - - cell->draw( fg_color, highlight_color ); - } - LLUI::popMatrix(); - - cur_x += cell->getWidth() + column_padding; - } -} - +/** + * @file llscrolllistitem.cpp + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). + * + * $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 "linden_common.h" + +#include "llscrolllistitem.h" + +#include "llrect.h" +#include "llui.h" + + +//--------------------------------------------------------------------------- +// LLScrollListItem +//--------------------------------------------------------------------------- + +LLScrollListItem::LLScrollListItem( const Params& p ) +: mSelected(false), + mHighlighted(false), + mHoverIndex(-1), + mSelectedIndex(-1), + mEnabled(p.enabled), + mUserdata(p.userdata), + mItemValue(p.value), + mItemAltValue(p.alt_value) +{ +} + + +LLScrollListItem::~LLScrollListItem() +{ + std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); + mColumns.clear(); +} + +void LLScrollListItem::setSelected(bool b) +{ + mSelected = b; + mSelectedIndex = -1; +} + +void LLScrollListItem::setHighlighted(bool b) +{ + mHighlighted = b; + mHoverIndex = -1; +} + +void LLScrollListItem::setHoverCell(S32 cell) +{ + mHoverIndex = cell; +} + +void LLScrollListItem::setSelectedCell(S32 cell) +{ + mSelectedIndex = cell; +} + +void LLScrollListItem::addColumn(const LLScrollListCell::Params& p) +{ + mColumns.push_back(LLScrollListCell::create(p)); +} + +void LLScrollListItem::setNumColumns(S32 columns) +{ + S32 prev_columns = mColumns.size(); + if (columns < prev_columns) + { + std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer()); + } + + mColumns.resize(columns); + + for (S32 col = prev_columns; col < columns; ++col) + { + mColumns[col] = NULL; + } +} + +void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell ) +{ + if (column < (S32)mColumns.size()) + { + delete mColumns[column]; + mColumns[column] = cell; + } + else + { + LL_ERRS() << "LLScrollListItem::setColumn: bad column: " << column << LL_ENDL; + } +} + + +S32 LLScrollListItem::getNumColumns() const +{ + return mColumns.size(); +} + +LLScrollListCell* LLScrollListItem::getColumn(const S32 i) const +{ + if (0 <= i && i < (S32)mColumns.size()) + { + return mColumns[i]; + } + return NULL; +} + +std::string LLScrollListItem::getContentsCSV() const +{ + std::string ret; + + S32 count = getNumColumns(); + for (S32 i=0; igetValue().asString(); + if (i < count-1) + { + ret += ", "; + } + } + + return ret; +} + + +void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& hover_color, const LLColor4& select_color, const LLColor4& highlight_color, S32 column_padding) +{ + // draw background rect + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + LLRect bg_rect = rect; + if (mSelectedIndex < 0 && getSelected()) + { + // Whole item is highlighted/selected + gl_rect_2d(bg_rect, select_color); + } + else if (mHoverIndex < 0) + { + // Whole item is highlighted/selected + gl_rect_2d(bg_rect, hover_color); + } + + S32 cur_x = rect.mLeft; + S32 num_cols = getNumColumns(); + S32 cur_col = 0; + + for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols; cell = getColumn(++cur_col)) + { + // Two ways a cell could be hidden + if (cell->getWidth() < 0 + || !cell->getVisible()) continue; + + LLUI::pushMatrix(); + { + LLUI::translate((F32) cur_x, (F32) rect.mBottom); + + if (mSelectedIndex == cur_col) + { + // select specific cell + LLRect highlight_rect(0, + cell->getHeight(), + cell->getWidth(), + 0); + gl_rect_2d(highlight_rect, select_color); + } + else if (mHoverIndex == cur_col) + { + // highlight specific cell + LLRect highlight_rect(0, + cell->getHeight(), + cell->getWidth() , + 0); + gl_rect_2d(highlight_rect, hover_color); + } + + cell->draw( fg_color, highlight_color ); + } + LLUI::popMatrix(); + + cur_x += cell->getWidth() + column_padding; + } +} + diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h index 3e9e6e084c..ee8a8bb556 100644 --- a/indra/llui/llscrolllistitem.h +++ b/indra/llui/llscrolllistitem.h @@ -1,142 +1,142 @@ -/** - * @file llscrolllistitem.h - * @brief Scroll lists are composed of rows (items), each of which - * contains columns (cells). - * - * $LicenseInfo:firstyear=2007&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 LLSCROLLLISTITEM_H -#define LLSCROLLLISTITEM_H - -#include "llpointer.h" // LLPointer<> -#include "llsd.h" -#include "v4color.h" -#include "llinitparam.h" -#include "llscrolllistcell.h" -#include "llcoord.h" - -#include - -class LLCheckBoxCtrl; -class LLResizeBar; -class LLScrollListCtrl; -class LLScrollColumnHeader; -class LLUIImage; - -//--------------------------------------------------------------------------- -// LLScrollListItem -//--------------------------------------------------------------------------- -class LLScrollListItem -{ - friend class LLScrollListCtrl; -public: - struct Params : public LLInitParam::Block - { - Optional enabled; - Optional userdata; - Optional value; - Optional alt_value; - - Ignored name; // use for localization tools - Ignored type; - Ignored length; - - Multiple columns; - - Params() - : enabled("enabled", true), - value("value"), - alt_value("alt_value"), - name("name"), - type("type"), - length("length"), - columns("columns") - { - addSynonym(columns, "column"); - addSynonym(value, "id"); - } - }; - - virtual ~LLScrollListItem(); - - void setSelected( bool b ); - bool getSelected() const { return mSelected; } - - void setEnabled( bool b ) { mEnabled = b; } - bool getEnabled() const { return mEnabled; } - - void setHighlighted( bool b ); - bool getHighlighted() const { return mHighlighted; } - - void setSelectedCell( S32 cell ); - S32 getSelectedCell() const { return mSelectedIndex; } - - void setHoverCell( S32 cell ); - S32 getHoverCell() const { return mHoverIndex; } - - void setUserdata( void* userdata ) { mUserdata = userdata; } - void* getUserdata() const { return mUserdata; } - - virtual LLUUID getUUID() const { return mItemValue.asUUID(); } - LLSD getValue() const { return mItemValue; } - LLSD getAltValue() const { return mItemAltValue; } - - void setRect(LLRect rect) { mRectangle = rect; } - LLRect getRect() const { return mRectangle; } - - void addColumn( const LLScrollListCell::Params& p ); - - void setNumColumns(S32 columns); - - void setColumn( S32 column, LLScrollListCell *cell ); - - S32 getNumColumns() const; - - LLScrollListCell *getColumn(const S32 i) const; - - std::string getContentsCSV() const; - - virtual void draw(const LLRect& rect, - const LLColor4& fg_color, - const LLColor4& hover_color, // highlight/hover selection of whole item or cell - const LLColor4& select_color, // highlight/hover selection of whole item or cell - const LLColor4& highlight_color, // highlights contents of cells (ex: text) - S32 column_padding); - -protected: - LLScrollListItem( const Params& ); - -private: - bool mSelected; - bool mHighlighted; - S32 mHoverIndex; - S32 mSelectedIndex; - bool mEnabled; - void* mUserdata; - LLSD mItemValue; - LLSD mItemAltValue; - std::vector mColumns; - LLRect mRectangle; -}; - -#endif +/** + * @file llscrolllistitem.h + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). + * + * $LicenseInfo:firstyear=2007&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 LLSCROLLLISTITEM_H +#define LLSCROLLLISTITEM_H + +#include "llpointer.h" // LLPointer<> +#include "llsd.h" +#include "v4color.h" +#include "llinitparam.h" +#include "llscrolllistcell.h" +#include "llcoord.h" + +#include + +class LLCheckBoxCtrl; +class LLResizeBar; +class LLScrollListCtrl; +class LLScrollColumnHeader; +class LLUIImage; + +//--------------------------------------------------------------------------- +// LLScrollListItem +//--------------------------------------------------------------------------- +class LLScrollListItem +{ + friend class LLScrollListCtrl; +public: + struct Params : public LLInitParam::Block + { + Optional enabled; + Optional userdata; + Optional value; + Optional alt_value; + + Ignored name; // use for localization tools + Ignored type; + Ignored length; + + Multiple columns; + + Params() + : enabled("enabled", true), + value("value"), + alt_value("alt_value"), + name("name"), + type("type"), + length("length"), + columns("columns") + { + addSynonym(columns, "column"); + addSynonym(value, "id"); + } + }; + + virtual ~LLScrollListItem(); + + void setSelected( bool b ); + bool getSelected() const { return mSelected; } + + void setEnabled( bool b ) { mEnabled = b; } + bool getEnabled() const { return mEnabled; } + + void setHighlighted( bool b ); + bool getHighlighted() const { return mHighlighted; } + + void setSelectedCell( S32 cell ); + S32 getSelectedCell() const { return mSelectedIndex; } + + void setHoverCell( S32 cell ); + S32 getHoverCell() const { return mHoverIndex; } + + void setUserdata( void* userdata ) { mUserdata = userdata; } + void* getUserdata() const { return mUserdata; } + + virtual LLUUID getUUID() const { return mItemValue.asUUID(); } + LLSD getValue() const { return mItemValue; } + LLSD getAltValue() const { return mItemAltValue; } + + void setRect(LLRect rect) { mRectangle = rect; } + LLRect getRect() const { return mRectangle; } + + void addColumn( const LLScrollListCell::Params& p ); + + void setNumColumns(S32 columns); + + void setColumn( S32 column, LLScrollListCell *cell ); + + S32 getNumColumns() const; + + LLScrollListCell *getColumn(const S32 i) const; + + std::string getContentsCSV() const; + + virtual void draw(const LLRect& rect, + const LLColor4& fg_color, + const LLColor4& hover_color, // highlight/hover selection of whole item or cell + const LLColor4& select_color, // highlight/hover selection of whole item or cell + const LLColor4& highlight_color, // highlights contents of cells (ex: text) + S32 column_padding); + +protected: + LLScrollListItem( const Params& ); + +private: + bool mSelected; + bool mHighlighted; + S32 mHoverIndex; + S32 mSelectedIndex; + bool mEnabled; + void* mUserdata; + LLSD mItemValue; + LLSD mItemAltValue; + std::vector mColumns; + LLRect mRectangle; +}; + +#endif diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index 6229bf031a..a0c1e9d0c0 100644 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -1,218 +1,218 @@ -/** - * @file llsearcheditor.cpp - * @brief LLSearchEditor 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$ - */ - -// Text editor widget to let users enter a single line. - -#include "linden_common.h" - -#include "llsearcheditor.h" -#include "llkeyboard.h" - -LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p) -: LLUICtrl(p), - mSearchButton(NULL), - mClearButton(NULL), - mEditorImage(p.background_image), - mEditorImageFocused(p.background_image_focused), - mEditorSearchImage(p.background_image_highlight), - mHighlightTextField(p.highlight_text_field) -{ - S32 srch_btn_top = p.search_button.top_pad + p.search_button.rect.height; - S32 srch_btn_right = p.search_button.rect.width + p.search_button.left_pad; - LLRect srch_btn_rect(p.search_button.left_pad, srch_btn_top, srch_btn_right, p.search_button.top_pad); - - S32 clr_btn_top = p.clear_button.rect.bottom + p.clear_button.rect.height; - S32 clr_btn_right = getRect().getWidth() - p.clear_button.pad_right; - S32 clr_btn_left = clr_btn_right - p.clear_button.rect.width; - LLRect clear_btn_rect(clr_btn_left, clr_btn_top, clr_btn_right, p.clear_button.rect.bottom); - - S32 text_pad_left = p.text_pad_left; - S32 text_pad_right = p.text_pad_right; - - if (p.search_button_visible) - text_pad_left += srch_btn_rect.getWidth(); - - if (p.clear_button_visible) - text_pad_right = getRect().getWidth() - clr_btn_left + p.clear_button.pad_left; - - // Set up line editor. - LLLineEditor::Params line_editor_params(p); - line_editor_params.name("filter edit box"); - line_editor_params.background_image(p.background_image); - line_editor_params.background_image_focused(p.background_image_focused); - line_editor_params.rect(getLocalRect()); - line_editor_params.follows.flags(FOLLOWS_ALL); - line_editor_params.text_pad_left(text_pad_left); - line_editor_params.text_pad_right(text_pad_right); - line_editor_params.revert_on_esc(false); - line_editor_params.commit_callback.function(boost::bind(&LLUICtrl::onCommit, this)); - line_editor_params.keystroke_callback(boost::bind(&LLSearchEditor::handleKeystroke, this)); - - mSearchEditor = LLUICtrlFactory::create(line_editor_params); - mSearchEditor->setPassDelete(true); - addChild(mSearchEditor); - - if (p.search_button_visible) - { - // Set up search button. - LLButton::Params srch_btn_params(p.search_button); - srch_btn_params.name(std::string("search button")); - srch_btn_params.rect(srch_btn_rect) ; - srch_btn_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP); - srch_btn_params.tab_stop(false); - srch_btn_params.click_callback.function(boost::bind(&LLUICtrl::onCommit, this)); - - mSearchButton = LLUICtrlFactory::create(srch_btn_params); - mSearchEditor->addChild(mSearchButton); - } - - if (p.clear_button_visible) - { - // Set up clear button. - LLButton::Params clr_btn_params(p.clear_button); - clr_btn_params.name(std::string("clear button")); - clr_btn_params.rect(clear_btn_rect) ; - clr_btn_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP); - clr_btn_params.tab_stop(false); - clr_btn_params.click_callback.function(boost::bind(&LLSearchEditor::onClearButtonClick, this, _2)); - - mClearButton = LLUICtrlFactory::create(clr_btn_params); - mSearchEditor->addChild(mClearButton); - } -} - -LLSearchEditor::~LLSearchEditor() -{ - mKeystrokeCallback = NULL; - mTextChangedCallback = NULL; - setCommitOnFocusLost(false); - - mSearchButton = NULL; - mClearButton = NULL; - mSearchEditor->deleteAllChildren(); - deleteAllChildren(); -} - -//virtual -void LLSearchEditor::draw() -{ - if (mClearButton) - mClearButton->setVisible(!mSearchEditor->getWText().empty()); - - if (mHighlightTextField) - { - if (!mSearchEditor->getWText().empty()) - { - mSearchEditor->setBgImage(mEditorSearchImage); - mSearchEditor->setBgImageFocused(mEditorSearchImage); - } - else - { - mSearchEditor->setBgImage(mEditorImage); - mSearchEditor->setBgImageFocused(mEditorImageFocused); - } - } - - LLUICtrl::draw(); -} - -//virtual -void LLSearchEditor::setValue(const LLSD& value ) -{ - mSearchEditor->setValue(value); -} - -//virtual -LLSD LLSearchEditor::getValue() const -{ - return mSearchEditor->getValue(); -} - -//virtual -bool LLSearchEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) -{ - return mSearchEditor->setTextArg(key, text); -} - -//virtual -bool LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - return mSearchEditor->setLabelArg(key, text); -} - -//virtual -void LLSearchEditor::setLabel( const LLStringExplicit &new_label ) -{ - mSearchEditor->setLabel(new_label); -} - -//virtual -void LLSearchEditor::clear() -{ - if (mSearchEditor) - { - mSearchEditor->clear(); - } -} - -//virtual -void LLSearchEditor::setFocus( bool b ) -{ - if (mSearchEditor) - { - mSearchEditor->setFocus(b); - } -} - -void LLSearchEditor::onClearButtonClick(const LLSD& data) -{ - setText(LLStringUtil::null); - if (mTextChangedCallback) - { - mTextChangedCallback(this, getValue()); - } - mSearchEditor->onCommit(); // force keystroke callback -} - -void LLSearchEditor::handleKeystroke() -{ - if (mKeystrokeCallback) - { - mKeystrokeCallback(this, getValue()); - } - - KEY key = gKeyboard->currentKey(); - if (key == KEY_LEFT || - key == KEY_RIGHT) - { - return; - } - - if (mTextChangedCallback) - { - mTextChangedCallback(this, getValue()); - } -} +/** + * @file llsearcheditor.cpp + * @brief LLSearchEditor 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$ + */ + +// Text editor widget to let users enter a single line. + +#include "linden_common.h" + +#include "llsearcheditor.h" +#include "llkeyboard.h" + +LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p) +: LLUICtrl(p), + mSearchButton(NULL), + mClearButton(NULL), + mEditorImage(p.background_image), + mEditorImageFocused(p.background_image_focused), + mEditorSearchImage(p.background_image_highlight), + mHighlightTextField(p.highlight_text_field) +{ + S32 srch_btn_top = p.search_button.top_pad + p.search_button.rect.height; + S32 srch_btn_right = p.search_button.rect.width + p.search_button.left_pad; + LLRect srch_btn_rect(p.search_button.left_pad, srch_btn_top, srch_btn_right, p.search_button.top_pad); + + S32 clr_btn_top = p.clear_button.rect.bottom + p.clear_button.rect.height; + S32 clr_btn_right = getRect().getWidth() - p.clear_button.pad_right; + S32 clr_btn_left = clr_btn_right - p.clear_button.rect.width; + LLRect clear_btn_rect(clr_btn_left, clr_btn_top, clr_btn_right, p.clear_button.rect.bottom); + + S32 text_pad_left = p.text_pad_left; + S32 text_pad_right = p.text_pad_right; + + if (p.search_button_visible) + text_pad_left += srch_btn_rect.getWidth(); + + if (p.clear_button_visible) + text_pad_right = getRect().getWidth() - clr_btn_left + p.clear_button.pad_left; + + // Set up line editor. + LLLineEditor::Params line_editor_params(p); + line_editor_params.name("filter edit box"); + line_editor_params.background_image(p.background_image); + line_editor_params.background_image_focused(p.background_image_focused); + line_editor_params.rect(getLocalRect()); + line_editor_params.follows.flags(FOLLOWS_ALL); + line_editor_params.text_pad_left(text_pad_left); + line_editor_params.text_pad_right(text_pad_right); + line_editor_params.revert_on_esc(false); + line_editor_params.commit_callback.function(boost::bind(&LLUICtrl::onCommit, this)); + line_editor_params.keystroke_callback(boost::bind(&LLSearchEditor::handleKeystroke, this)); + + mSearchEditor = LLUICtrlFactory::create(line_editor_params); + mSearchEditor->setPassDelete(true); + addChild(mSearchEditor); + + if (p.search_button_visible) + { + // Set up search button. + LLButton::Params srch_btn_params(p.search_button); + srch_btn_params.name(std::string("search button")); + srch_btn_params.rect(srch_btn_rect) ; + srch_btn_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP); + srch_btn_params.tab_stop(false); + srch_btn_params.click_callback.function(boost::bind(&LLUICtrl::onCommit, this)); + + mSearchButton = LLUICtrlFactory::create(srch_btn_params); + mSearchEditor->addChild(mSearchButton); + } + + if (p.clear_button_visible) + { + // Set up clear button. + LLButton::Params clr_btn_params(p.clear_button); + clr_btn_params.name(std::string("clear button")); + clr_btn_params.rect(clear_btn_rect) ; + clr_btn_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP); + clr_btn_params.tab_stop(false); + clr_btn_params.click_callback.function(boost::bind(&LLSearchEditor::onClearButtonClick, this, _2)); + + mClearButton = LLUICtrlFactory::create(clr_btn_params); + mSearchEditor->addChild(mClearButton); + } +} + +LLSearchEditor::~LLSearchEditor() +{ + mKeystrokeCallback = NULL; + mTextChangedCallback = NULL; + setCommitOnFocusLost(false); + + mSearchButton = NULL; + mClearButton = NULL; + mSearchEditor->deleteAllChildren(); + deleteAllChildren(); +} + +//virtual +void LLSearchEditor::draw() +{ + if (mClearButton) + mClearButton->setVisible(!mSearchEditor->getWText().empty()); + + if (mHighlightTextField) + { + if (!mSearchEditor->getWText().empty()) + { + mSearchEditor->setBgImage(mEditorSearchImage); + mSearchEditor->setBgImageFocused(mEditorSearchImage); + } + else + { + mSearchEditor->setBgImage(mEditorImage); + mSearchEditor->setBgImageFocused(mEditorImageFocused); + } + } + + LLUICtrl::draw(); +} + +//virtual +void LLSearchEditor::setValue(const LLSD& value ) +{ + mSearchEditor->setValue(value); +} + +//virtual +LLSD LLSearchEditor::getValue() const +{ + return mSearchEditor->getValue(); +} + +//virtual +bool LLSearchEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) +{ + return mSearchEditor->setTextArg(key, text); +} + +//virtual +bool LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + return mSearchEditor->setLabelArg(key, text); +} + +//virtual +void LLSearchEditor::setLabel( const LLStringExplicit &new_label ) +{ + mSearchEditor->setLabel(new_label); +} + +//virtual +void LLSearchEditor::clear() +{ + if (mSearchEditor) + { + mSearchEditor->clear(); + } +} + +//virtual +void LLSearchEditor::setFocus( bool b ) +{ + if (mSearchEditor) + { + mSearchEditor->setFocus(b); + } +} + +void LLSearchEditor::onClearButtonClick(const LLSD& data) +{ + setText(LLStringUtil::null); + if (mTextChangedCallback) + { + mTextChangedCallback(this, getValue()); + } + mSearchEditor->onCommit(); // force keystroke callback +} + +void LLSearchEditor::handleKeystroke() +{ + if (mKeystrokeCallback) + { + mKeystrokeCallback(this, getValue()); + } + + KEY key = gKeyboard->currentKey(); + if (key == KEY_LEFT || + key == KEY_RIGHT) + { + return; + } + + if (mTextChangedCallback) + { + mTextChangedCallback(this, getValue()); + } +} diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h index 7c6e5dc554..d99dc6f200 100644 --- a/indra/llui/llsearcheditor.h +++ b/indra/llui/llsearcheditor.h @@ -1,114 +1,114 @@ -/** - * @file llsearcheditor.h - * @brief Text editor widget that represents a search operation - * - * Features: - * Text entry of a single line (text, delete, left and right arrow, insert, return). - * Callbacks either on every keystroke or just on the return key. - * Focus (allow multiple text entry widgets) - * Clipboard (cut, copy, and paste) - * Horizontal scrolling to allow strings longer than widget size allows - * Pre-validation (limit which keys can be used) - * Optional line history so previous entries can be recalled by CTRL UP/DOWN - * - * $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$ - */ - -#ifndef LL_SEARCHEDITOR_H -#define LL_SEARCHEDITOR_H - -#include "lllineeditor.h" -#include "llbutton.h" - -class LLSearchEditor : public LLUICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Optional search_button, - clear_button; - Optional search_button_visible, - clear_button_visible, - highlight_text_field; - Optional keystroke_callback; - - Optional background_image, - background_image_focused, - background_image_highlight; - - Params() - : search_button("search_button"), - search_button_visible("search_button_visible"), - clear_button("clear_button"), - clear_button_visible("clear_button_visible"), - highlight_text_field("highlight_text_field"), - background_image("background_image"), - background_image_focused("background_image_focused"), - background_image_highlight("background_image_highlight") - {} - }; - - void setCommitOnFocusLost(bool b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); } - -protected: - LLSearchEditor(const Params&); - friend class LLUICtrlFactory; - -public: - virtual ~LLSearchEditor(); - - /*virtual*/ void draw(); - - void setText(const LLStringExplicit &new_text) { mSearchEditor->setText(new_text); } - const std::string& getText() const { return mSearchEditor->getText(); } - - // LLUICtrl interface - virtual void setValue(const LLSD& value ); - virtual LLSD getValue() const; - virtual bool setTextArg( const std::string& key, const LLStringExplicit& text ); - virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - virtual void setLabel( const LLStringExplicit &new_label ); - virtual void clear(); - virtual void setFocus( bool b ); - - void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; } - void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } - -protected: - void onClearButtonClick(const LLSD& data); - virtual void handleKeystroke(); - - commit_callback_t mKeystrokeCallback; - commit_callback_t mTextChangedCallback; - LLLineEditor* mSearchEditor; - LLButton* mSearchButton; - LLButton* mClearButton; - - LLPointer mEditorImage; - LLPointer mEditorImageFocused; - LLPointer mEditorSearchImage; - LLPointer mEditorSearchImageFocused; - - bool mHighlightTextField; -}; - -#endif // LL_SEARCHEDITOR_H +/** + * @file llsearcheditor.h + * @brief Text editor widget that represents a search operation + * + * Features: + * Text entry of a single line (text, delete, left and right arrow, insert, return). + * Callbacks either on every keystroke or just on the return key. + * Focus (allow multiple text entry widgets) + * Clipboard (cut, copy, and paste) + * Horizontal scrolling to allow strings longer than widget size allows + * Pre-validation (limit which keys can be used) + * Optional line history so previous entries can be recalled by CTRL UP/DOWN + * + * $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$ + */ + +#ifndef LL_SEARCHEDITOR_H +#define LL_SEARCHEDITOR_H + +#include "lllineeditor.h" +#include "llbutton.h" + +class LLSearchEditor : public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional search_button, + clear_button; + Optional search_button_visible, + clear_button_visible, + highlight_text_field; + Optional keystroke_callback; + + Optional background_image, + background_image_focused, + background_image_highlight; + + Params() + : search_button("search_button"), + search_button_visible("search_button_visible"), + clear_button("clear_button"), + clear_button_visible("clear_button_visible"), + highlight_text_field("highlight_text_field"), + background_image("background_image"), + background_image_focused("background_image_focused"), + background_image_highlight("background_image_highlight") + {} + }; + + void setCommitOnFocusLost(bool b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); } + +protected: + LLSearchEditor(const Params&); + friend class LLUICtrlFactory; + +public: + virtual ~LLSearchEditor(); + + /*virtual*/ void draw(); + + void setText(const LLStringExplicit &new_text) { mSearchEditor->setText(new_text); } + const std::string& getText() const { return mSearchEditor->getText(); } + + // LLUICtrl interface + virtual void setValue(const LLSD& value ); + virtual LLSD getValue() const; + virtual bool setTextArg( const std::string& key, const LLStringExplicit& text ); + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); + virtual void setLabel( const LLStringExplicit &new_label ); + virtual void clear(); + virtual void setFocus( bool b ); + + void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; } + void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } + +protected: + void onClearButtonClick(const LLSD& data); + virtual void handleKeystroke(); + + commit_callback_t mKeystrokeCallback; + commit_callback_t mTextChangedCallback; + LLLineEditor* mSearchEditor; + LLButton* mSearchButton; + LLButton* mClearButton; + + LLPointer mEditorImage; + LLPointer mEditorImageFocused; + LLPointer mEditorSearchImage; + LLPointer mEditorSearchImageFocused; + + bool mHighlightTextField; +}; + +#endif // LL_SEARCHEDITOR_H diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index f892816116..0507733fd8 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -1,383 +1,383 @@ -/** - * @file llslider.cpp - * @brief LLSlider base class - * - * $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$ - */ - -#include "linden_common.h" - -#include "llslider.h" -#include "llui.h" - -#include "llgl.h" -#include "llwindow.h" -#include "llfocusmgr.h" -#include "llkeyboard.h" // for the MASK constants -#include "llcontrol.h" -#include "lluictrlfactory.h" - -static LLDefaultChildRegistry::Register r1("slider_bar"); -//FIXME: make this into an unregistered template so that code constructed sliders don't -// have ambigious template lookup problem - -LLSlider::Params::Params() -: orientation ("orientation", std::string ("horizontal")), - thumb_outline_color("thumb_outline_color"), - thumb_center_color("thumb_center_color"), - thumb_image("thumb_image"), - thumb_image_pressed("thumb_image_pressed"), - thumb_image_disabled("thumb_image_disabled"), - track_image_horizontal("track_image_horizontal"), - track_image_vertical("track_image_vertical"), - track_highlight_horizontal_image("track_highlight_horizontal_image"), - track_highlight_vertical_image("track_highlight_vertical_image"), - mouse_down_callback("mouse_down_callback"), - mouse_up_callback("mouse_up_callback") -{} - -LLSlider::LLSlider(const LLSlider::Params& p) -: LLF32UICtrl(p), - mMouseOffset( 0 ), - mOrientation ((p.orientation() == "horizontal") ? HORIZONTAL : VERTICAL), - mThumbOutlineColor(p.thumb_outline_color()), - mThumbCenterColor(p.thumb_center_color()), - mThumbImage(p.thumb_image), - mThumbImagePressed(p.thumb_image_pressed), - mThumbImageDisabled(p.thumb_image_disabled), - mTrackImageHorizontal(p.track_image_horizontal), - mTrackImageVertical(p.track_image_vertical), - mTrackHighlightHorizontalImage(p.track_highlight_horizontal_image), - mTrackHighlightVerticalImage(p.track_highlight_vertical_image), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL) -{ - mViewModel->setValue(p.initial_value); - updateThumbRect(); - mDragStartThumbRect = mThumbRect; - setControlName(p.control_name, NULL); - setValue(getValueF32()); - - if (p.mouse_down_callback.isProvided()) - { - setMouseDownCallback(initCommitCallback(p.mouse_down_callback)); - } - if (p.mouse_up_callback.isProvided()) - { - setMouseUpCallback(initCommitCallback(p.mouse_up_callback)); - } -} - -LLSlider::~LLSlider() -{ - delete mMouseDownSignal; - delete mMouseUpSignal; -} - -void LLSlider::setValue(F32 value, bool from_event) -{ - value = llclamp( value, mMinValue, mMaxValue ); - - // Round to nearest increment (bias towards rounding down) - value -= mMinValue; - value += mIncrement/2.0001f; - value -= fmod(value, mIncrement); - value += mMinValue; - - if (!from_event && getValueF32() != value) - { - setControlValue(value); - } - - LLF32UICtrl::setValue(value); - updateThumbRect(); -} - -void LLSlider::updateThumbRect() -{ - const S32 DEFAULT_THUMB_SIZE = 16; - F32 t = (getValueF32() - mMinValue) / (mMaxValue - mMinValue); - - S32 thumb_width = mThumbImage ? mThumbImage->getWidth() : DEFAULT_THUMB_SIZE; - S32 thumb_height = mThumbImage ? mThumbImage->getHeight() : DEFAULT_THUMB_SIZE; - - if ( mOrientation == HORIZONTAL ) - { - S32 left_edge = (thumb_width / 2); - S32 right_edge = getRect().getWidth() - (thumb_width / 2); - - S32 x = left_edge + S32( t * (right_edge - left_edge) ); - mThumbRect.mLeft = x - (thumb_width / 2); - mThumbRect.mRight = mThumbRect.mLeft + thumb_width; - mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2); - mThumbRect.mTop = mThumbRect.mBottom + thumb_height; - } - else - { - S32 top_edge = (thumb_height / 2); - S32 bottom_edge = getRect().getHeight() - (thumb_height / 2); - - S32 y = top_edge + S32( t * (bottom_edge - top_edge) ); - mThumbRect.mLeft = getLocalRect().getCenterX() - (thumb_width / 2); - mThumbRect.mRight = mThumbRect.mLeft + thumb_width; - mThumbRect.mBottom = y - (thumb_height / 2); - mThumbRect.mTop = mThumbRect.mBottom + thumb_height; - } -} - - -void LLSlider::setValueAndCommit(F32 value) -{ - F32 old_value = getValueF32(); - setValue(value); - - if (getValueF32() != old_value) - { - onCommit(); - } -} - - -bool LLSlider::handleHover(S32 x, S32 y, MASK mask) -{ - if( hasMouseCapture() ) - { - if ( mOrientation == HORIZONTAL ) - { - S32 thumb_half_width = mThumbImage->getWidth()/2; - S32 left_edge = thumb_half_width; - S32 right_edge = getRect().getWidth() - (thumb_half_width); - - x += mMouseOffset; - x = llclamp( x, left_edge, right_edge ); - - F32 t = F32(x - left_edge) / (right_edge - left_edge); - setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue ); - } - else // mOrientation == VERTICAL - { - S32 thumb_half_height = mThumbImage->getHeight()/2; - S32 top_edge = thumb_half_height; - S32 bottom_edge = getRect().getHeight() - (thumb_half_height); - - y += mMouseOffset; - y = llclamp(y, top_edge, bottom_edge); - - F32 t = F32(y - top_edge) / (bottom_edge - top_edge); - setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue ); - } - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - } - else - { - getWindow()->setCursor(UI_CURSOR_ARROW); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - } - return true; -} - -bool LLSlider::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - if( hasMouseCapture() ) - { - gFocusMgr.setMouseCapture( NULL ); - - if (mMouseUpSignal) - (*mMouseUpSignal)( this, getValueF32() ); - - handled = true; - make_ui_sound("UISndClickRelease"); - } - else - { - handled = true; - } - - return handled; -} - -bool LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // only do sticky-focus on non-chrome widgets - if (!getIsChrome()) - { - setFocus(true); - } - if (mMouseDownSignal) - (*mMouseDownSignal)( this, getValueF32() ); - - if (MASK_CONTROL & mask) // if CTRL is modifying - { - setValueAndCommit(mInitialValue); - } - else - { - // Find the offset of the actual mouse location from the center of the thumb. - if (mThumbRect.pointInRect(x,y)) - { - mMouseOffset = (mOrientation == HORIZONTAL) - ? (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x - : (mThumbRect.mBottom + mThumbImage->getHeight()/2) - y; - } - else - { - mMouseOffset = 0; - } - - // Start dragging the thumb - // No handler needed for focus lost since this class has no state that depends on it. - gFocusMgr.setMouseCapture( this ); - mDragStartThumbRect = mThumbRect; - } - make_ui_sound("UISndClick"); - - return true; -} - -bool LLSlider::handleKeyHere(KEY key, MASK mask) -{ - bool handled = false; - switch(key) - { - case KEY_DOWN: - case KEY_LEFT: - setValueAndCommit(getValueF32() - getIncrement()); - handled = true; - break; - case KEY_UP: - case KEY_RIGHT: - setValueAndCommit(getValueF32() + getIncrement()); - handled = true; - break; - default: - break; - } - return handled; -} - -bool LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if ( mOrientation == VERTICAL ) - { - F32 new_val = getValueF32() - clicks * getIncrement(); - setValueAndCommit(new_val); - return true; - } - return LLF32UICtrl::handleScrollWheel(x,y,clicks); -} - -void LLSlider::draw() -{ - F32 alpha = getDrawContext().mAlpha; - - // since thumb image might still be decoding, need thumb to accomodate image size - updateThumbRect(); - - // Draw background and thumb. - - // drawing solids requires texturing be disabled - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - // Track - LLPointer& trackImage = ( mOrientation == HORIZONTAL ) - ? mTrackImageHorizontal - : mTrackImageVertical; - - LLPointer& trackHighlightImage = ( mOrientation == HORIZONTAL ) - ? mTrackHighlightHorizontalImage - : mTrackHighlightVerticalImage; - - LLRect track_rect; - LLRect highlight_rect; - - if ( mOrientation == HORIZONTAL ) - { - track_rect.set(mThumbImage->getWidth() / 2, - getLocalRect().getCenterY() + (trackImage->getHeight() / 2), - getRect().getWidth() - mThumbImage->getWidth() / 2, - getLocalRect().getCenterY() - (trackImage->getHeight() / 2) ); - highlight_rect.set(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom); - } - else - { - track_rect.set(getLocalRect().getCenterX() - (trackImage->getWidth() / 2), - getRect().getHeight(), - getLocalRect().getCenterX() + (trackImage->getWidth() / 2), - 0); - highlight_rect.set(track_rect.mLeft, track_rect.mTop, track_rect.mRight, track_rect.mBottom); - } - - LLColor4 color = isInEnabledChain() ? LLColor4::white % alpha : LLColor4::white % (0.6f * alpha); - trackImage->draw(track_rect, color); - trackHighlightImage->draw(highlight_rect, color); - - // Thumb - if (hasFocus()) - { - // Draw focus highlighting. - mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth()); - } - - if( hasMouseCapture() ) // currently clicking on slider - { - // Show ghost where thumb was before dragging began. - if (mThumbImage.notNull()) - { - mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor.get() % (0.3f * alpha)); - } - if (mThumbImagePressed.notNull()) - { - mThumbImagePressed->draw(mThumbRect, mThumbOutlineColor % alpha); - } - } - else if (!isInEnabledChain()) - { - if (mThumbImageDisabled.notNull()) - { - mThumbImageDisabled->draw(mThumbRect, mThumbCenterColor % alpha); - } - } - else - { - if (mThumbImage.notNull()) - { - mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha); - } - } - - LLUICtrl::draw(); -} - -boost::signals2::connection LLSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t(); - return mMouseDownSignal->connect(cb); -} - -boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t(); - return mMouseUpSignal->connect(cb); -} +/** + * @file llslider.cpp + * @brief LLSlider base class + * + * $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$ + */ + +#include "linden_common.h" + +#include "llslider.h" +#include "llui.h" + +#include "llgl.h" +#include "llwindow.h" +#include "llfocusmgr.h" +#include "llkeyboard.h" // for the MASK constants +#include "llcontrol.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register r1("slider_bar"); +//FIXME: make this into an unregistered template so that code constructed sliders don't +// have ambigious template lookup problem + +LLSlider::Params::Params() +: orientation ("orientation", std::string ("horizontal")), + thumb_outline_color("thumb_outline_color"), + thumb_center_color("thumb_center_color"), + thumb_image("thumb_image"), + thumb_image_pressed("thumb_image_pressed"), + thumb_image_disabled("thumb_image_disabled"), + track_image_horizontal("track_image_horizontal"), + track_image_vertical("track_image_vertical"), + track_highlight_horizontal_image("track_highlight_horizontal_image"), + track_highlight_vertical_image("track_highlight_vertical_image"), + mouse_down_callback("mouse_down_callback"), + mouse_up_callback("mouse_up_callback") +{} + +LLSlider::LLSlider(const LLSlider::Params& p) +: LLF32UICtrl(p), + mMouseOffset( 0 ), + mOrientation ((p.orientation() == "horizontal") ? HORIZONTAL : VERTICAL), + mThumbOutlineColor(p.thumb_outline_color()), + mThumbCenterColor(p.thumb_center_color()), + mThumbImage(p.thumb_image), + mThumbImagePressed(p.thumb_image_pressed), + mThumbImageDisabled(p.thumb_image_disabled), + mTrackImageHorizontal(p.track_image_horizontal), + mTrackImageVertical(p.track_image_vertical), + mTrackHighlightHorizontalImage(p.track_highlight_horizontal_image), + mTrackHighlightVerticalImage(p.track_highlight_vertical_image), + mMouseDownSignal(NULL), + mMouseUpSignal(NULL) +{ + mViewModel->setValue(p.initial_value); + updateThumbRect(); + mDragStartThumbRect = mThumbRect; + setControlName(p.control_name, NULL); + setValue(getValueF32()); + + if (p.mouse_down_callback.isProvided()) + { + setMouseDownCallback(initCommitCallback(p.mouse_down_callback)); + } + if (p.mouse_up_callback.isProvided()) + { + setMouseUpCallback(initCommitCallback(p.mouse_up_callback)); + } +} + +LLSlider::~LLSlider() +{ + delete mMouseDownSignal; + delete mMouseUpSignal; +} + +void LLSlider::setValue(F32 value, bool from_event) +{ + value = llclamp( value, mMinValue, mMaxValue ); + + // Round to nearest increment (bias towards rounding down) + value -= mMinValue; + value += mIncrement/2.0001f; + value -= fmod(value, mIncrement); + value += mMinValue; + + if (!from_event && getValueF32() != value) + { + setControlValue(value); + } + + LLF32UICtrl::setValue(value); + updateThumbRect(); +} + +void LLSlider::updateThumbRect() +{ + const S32 DEFAULT_THUMB_SIZE = 16; + F32 t = (getValueF32() - mMinValue) / (mMaxValue - mMinValue); + + S32 thumb_width = mThumbImage ? mThumbImage->getWidth() : DEFAULT_THUMB_SIZE; + S32 thumb_height = mThumbImage ? mThumbImage->getHeight() : DEFAULT_THUMB_SIZE; + + if ( mOrientation == HORIZONTAL ) + { + S32 left_edge = (thumb_width / 2); + S32 right_edge = getRect().getWidth() - (thumb_width / 2); + + S32 x = left_edge + S32( t * (right_edge - left_edge) ); + mThumbRect.mLeft = x - (thumb_width / 2); + mThumbRect.mRight = mThumbRect.mLeft + thumb_width; + mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2); + mThumbRect.mTop = mThumbRect.mBottom + thumb_height; + } + else + { + S32 top_edge = (thumb_height / 2); + S32 bottom_edge = getRect().getHeight() - (thumb_height / 2); + + S32 y = top_edge + S32( t * (bottom_edge - top_edge) ); + mThumbRect.mLeft = getLocalRect().getCenterX() - (thumb_width / 2); + mThumbRect.mRight = mThumbRect.mLeft + thumb_width; + mThumbRect.mBottom = y - (thumb_height / 2); + mThumbRect.mTop = mThumbRect.mBottom + thumb_height; + } +} + + +void LLSlider::setValueAndCommit(F32 value) +{ + F32 old_value = getValueF32(); + setValue(value); + + if (getValueF32() != old_value) + { + onCommit(); + } +} + + +bool LLSlider::handleHover(S32 x, S32 y, MASK mask) +{ + if( hasMouseCapture() ) + { + if ( mOrientation == HORIZONTAL ) + { + S32 thumb_half_width = mThumbImage->getWidth()/2; + S32 left_edge = thumb_half_width; + S32 right_edge = getRect().getWidth() - (thumb_half_width); + + x += mMouseOffset; + x = llclamp( x, left_edge, right_edge ); + + F32 t = F32(x - left_edge) / (right_edge - left_edge); + setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue ); + } + else // mOrientation == VERTICAL + { + S32 thumb_half_height = mThumbImage->getHeight()/2; + S32 top_edge = thumb_half_height; + S32 bottom_edge = getRect().getHeight() - (thumb_half_height); + + y += mMouseOffset; + y = llclamp(y, top_edge, bottom_edge); + + F32 t = F32(y - top_edge) / (bottom_edge - top_edge); + setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue ); + } + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; + } + else + { + getWindow()->setCursor(UI_CURSOR_ARROW); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; + } + return true; +} + +bool LLSlider::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if( hasMouseCapture() ) + { + gFocusMgr.setMouseCapture( NULL ); + + if (mMouseUpSignal) + (*mMouseUpSignal)( this, getValueF32() ); + + handled = true; + make_ui_sound("UISndClickRelease"); + } + else + { + handled = true; + } + + return handled; +} + +bool LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // only do sticky-focus on non-chrome widgets + if (!getIsChrome()) + { + setFocus(true); + } + if (mMouseDownSignal) + (*mMouseDownSignal)( this, getValueF32() ); + + if (MASK_CONTROL & mask) // if CTRL is modifying + { + setValueAndCommit(mInitialValue); + } + else + { + // Find the offset of the actual mouse location from the center of the thumb. + if (mThumbRect.pointInRect(x,y)) + { + mMouseOffset = (mOrientation == HORIZONTAL) + ? (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x + : (mThumbRect.mBottom + mThumbImage->getHeight()/2) - y; + } + else + { + mMouseOffset = 0; + } + + // Start dragging the thumb + // No handler needed for focus lost since this class has no state that depends on it. + gFocusMgr.setMouseCapture( this ); + mDragStartThumbRect = mThumbRect; + } + make_ui_sound("UISndClick"); + + return true; +} + +bool LLSlider::handleKeyHere(KEY key, MASK mask) +{ + bool handled = false; + switch(key) + { + case KEY_DOWN: + case KEY_LEFT: + setValueAndCommit(getValueF32() - getIncrement()); + handled = true; + break; + case KEY_UP: + case KEY_RIGHT: + setValueAndCommit(getValueF32() + getIncrement()); + handled = true; + break; + default: + break; + } + return handled; +} + +bool LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if ( mOrientation == VERTICAL ) + { + F32 new_val = getValueF32() - clicks * getIncrement(); + setValueAndCommit(new_val); + return true; + } + return LLF32UICtrl::handleScrollWheel(x,y,clicks); +} + +void LLSlider::draw() +{ + F32 alpha = getDrawContext().mAlpha; + + // since thumb image might still be decoding, need thumb to accomodate image size + updateThumbRect(); + + // Draw background and thumb. + + // drawing solids requires texturing be disabled + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // Track + LLPointer& trackImage = ( mOrientation == HORIZONTAL ) + ? mTrackImageHorizontal + : mTrackImageVertical; + + LLPointer& trackHighlightImage = ( mOrientation == HORIZONTAL ) + ? mTrackHighlightHorizontalImage + : mTrackHighlightVerticalImage; + + LLRect track_rect; + LLRect highlight_rect; + + if ( mOrientation == HORIZONTAL ) + { + track_rect.set(mThumbImage->getWidth() / 2, + getLocalRect().getCenterY() + (trackImage->getHeight() / 2), + getRect().getWidth() - mThumbImage->getWidth() / 2, + getLocalRect().getCenterY() - (trackImage->getHeight() / 2) ); + highlight_rect.set(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom); + } + else + { + track_rect.set(getLocalRect().getCenterX() - (trackImage->getWidth() / 2), + getRect().getHeight(), + getLocalRect().getCenterX() + (trackImage->getWidth() / 2), + 0); + highlight_rect.set(track_rect.mLeft, track_rect.mTop, track_rect.mRight, track_rect.mBottom); + } + + LLColor4 color = isInEnabledChain() ? LLColor4::white % alpha : LLColor4::white % (0.6f * alpha); + trackImage->draw(track_rect, color); + trackHighlightImage->draw(highlight_rect, color); + + // Thumb + if (hasFocus()) + { + // Draw focus highlighting. + mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth()); + } + + if( hasMouseCapture() ) // currently clicking on slider + { + // Show ghost where thumb was before dragging began. + if (mThumbImage.notNull()) + { + mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor.get() % (0.3f * alpha)); + } + if (mThumbImagePressed.notNull()) + { + mThumbImagePressed->draw(mThumbRect, mThumbOutlineColor % alpha); + } + } + else if (!isInEnabledChain()) + { + if (mThumbImageDisabled.notNull()) + { + mThumbImageDisabled->draw(mThumbRect, mThumbCenterColor % alpha); + } + } + else + { + if (mThumbImage.notNull()) + { + mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha); + } + } + + LLUICtrl::draw(); +} + +boost::signals2::connection LLSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t(); + return mMouseDownSignal->connect(cb); +} + +boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t(); + return mMouseUpSignal->connect(cb); +} diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index 9cd1c9cee4..03fad5a05d 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -1,108 +1,108 @@ -/** - * @file llslider.h - * @brief A simple slider with no label. - * - * $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_LLSLIDER_H -#define LL_LLSLIDER_H - -#include "llf32uictrl.h" -#include "v4color.h" -#include "lluiimage.h" - -class LLSlider : public LLF32UICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Optional orientation; - - Optional thumb_outline_color, - thumb_center_color; - - Optional thumb_image, - thumb_image_pressed, - thumb_image_disabled, - track_image_horizontal, - track_image_vertical, - track_highlight_horizontal_image, - track_highlight_vertical_image; - - Optional mouse_down_callback, - mouse_up_callback; - - - Params(); - }; -protected: - LLSlider(const Params&); - friend class LLUICtrlFactory; -public: - virtual ~LLSlider(); - void setValue( F32 value, bool from_event = false ); - // overrides for LLF32UICtrl methods - virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), true); } - - virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } - virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } - virtual void setMinValue(F32 min_value) { LLF32UICtrl::setMinValue(min_value); updateThumbRect(); } - virtual void setMaxValue(F32 max_value) { LLF32UICtrl::setMaxValue(max_value); updateThumbRect(); } - - boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); - - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleKeyHere(KEY key, MASK mask); - virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); - virtual void draw(); - -private: - void setValueAndCommit(F32 value); - void updateThumbRect(); - - bool mVolumeSlider; - S32 mMouseOffset; - LLRect mDragStartThumbRect; - - LLPointer mThumbImage; - LLPointer mThumbImagePressed; - LLPointer mThumbImageDisabled; - LLPointer mTrackImageHorizontal; - LLPointer mTrackImageVertical; - LLPointer mTrackHighlightHorizontalImage; - LLPointer mTrackHighlightVerticalImage; - - const EOrientation mOrientation; - - LLRect mThumbRect; - LLUIColor mThumbOutlineColor; - LLUIColor mThumbCenterColor; - - commit_signal_t* mMouseDownSignal; - commit_signal_t* mMouseUpSignal; -}; - -#endif // LL_LLSLIDER_H +/** + * @file llslider.h + * @brief A simple slider with no label. + * + * $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_LLSLIDER_H +#define LL_LLSLIDER_H + +#include "llf32uictrl.h" +#include "v4color.h" +#include "lluiimage.h" + +class LLSlider : public LLF32UICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional orientation; + + Optional thumb_outline_color, + thumb_center_color; + + Optional thumb_image, + thumb_image_pressed, + thumb_image_disabled, + track_image_horizontal, + track_image_vertical, + track_highlight_horizontal_image, + track_highlight_vertical_image; + + Optional mouse_down_callback, + mouse_up_callback; + + + Params(); + }; +protected: + LLSlider(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLSlider(); + void setValue( F32 value, bool from_event = false ); + // overrides for LLF32UICtrl methods + virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), true); } + + virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } + virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } + virtual void setMinValue(F32 min_value) { LLF32UICtrl::setMinValue(min_value); updateThumbRect(); } + virtual void setMaxValue(F32 max_value) { LLF32UICtrl::setMaxValue(max_value); updateThumbRect(); } + + boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); + + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); + virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks); + virtual void draw(); + +private: + void setValueAndCommit(F32 value); + void updateThumbRect(); + + bool mVolumeSlider; + S32 mMouseOffset; + LLRect mDragStartThumbRect; + + LLPointer mThumbImage; + LLPointer mThumbImagePressed; + LLPointer mThumbImageDisabled; + LLPointer mTrackImageHorizontal; + LLPointer mTrackImageVertical; + LLPointer mTrackHighlightHorizontalImage; + LLPointer mTrackHighlightVerticalImage; + + const EOrientation mOrientation; + + LLRect mThumbRect; + LLUIColor mThumbOutlineColor; + LLUIColor mThumbCenterColor; + + commit_signal_t* mMouseDownSignal; + commit_signal_t* mMouseUpSignal; +}; + +#endif // LL_LLSLIDER_H diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index e7e25716ad..22579205d8 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -1,489 +1,489 @@ -/** - * @file llsliderctrl.cpp - * @brief LLSliderCtrl base class - * - * $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$ - */ - -#include "linden_common.h" - -#include "llsliderctrl.h" - -#include "llmath.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llslider.h" -#include "llstring.h" -#include "lltextbox.h" -#include "llui.h" -#include "lluiconstants.h" -#include "llcontrol.h" -#include "llfocusmgr.h" -#include "llresmgr.h" -#include "lluictrlfactory.h" - -static LLDefaultChildRegistry::Register r("slider"); - -LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p) -: LLF32UICtrl(p), - mLabelBox( NULL ), - mEditor( NULL ), - mTextBox( NULL ), - mFont(p.font), - mShowText(p.show_text), - mCanEditText(p.can_edit_text), - mPrecision(p.decimal_digits), - mTextEnabledColor(p.text_color()), - mTextDisabledColor(p.text_disabled_color()), - mLabelWidth(p.label_width), - mEditorCommitSignal(NULL) -{ - S32 top = getRect().getHeight(); - S32 bottom = 0; - S32 left = 0; - - S32 label_width = p.label_width; - S32 text_width = p.text_width; - - // Label - if( !p.label().empty() ) - { - if (!p.label_width.isProvided()) - { - label_width = p.font()->getWidth(p.label); - } - LLRect label_rect( left, top, label_width, bottom ); - LLTextBox::Params params(p.slider_label); - if (!params.rect.isProvided()) - { - params.rect = label_rect; - } - if (!params.font.isProvided()) - { - params.font = p.font; - } - params.initial_value(p.label()); - mLabelBox = LLUICtrlFactory::create (params); - addChild(mLabelBox); - mLabelFont = params.font(); - } - - if (p.show_text && !p.text_width.isProvided()) - { - // calculate the size of the text box (log max_value is number of digits - 1 so plus 1) - if ( p.max_value ) - text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 ); - - if ( p.increment < 1.0f ) - text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value - - if ( p.min_value < 0.0f || p.max_value < 0.0f ) - text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign - - // padding to make things look nicer - text_width += 8; - } - - - S32 text_left = getRect().getWidth() - text_width; - static LLUICachedControl sliderctrl_spacing ("UISliderctrlSpacing", 0); - - S32 slider_right = getRect().getWidth(); - if( p.show_text ) - { - slider_right = text_left - sliderctrl_spacing; - } - - S32 slider_left = label_width ? label_width + sliderctrl_spacing : 0; - LLSlider::Params slider_p(p.slider_bar); - slider_p.name("slider_bar"); - if (!slider_p.rect.isProvided()) - { - slider_p.rect = LLRect(slider_left,top,slider_right,bottom); - } - if (!slider_p.initial_value.isProvided()) - { - slider_p.initial_value = p.initial_value().asReal(); - } - if (!slider_p.min_value.isProvided()) - { - slider_p.min_value = p.min_value; - } - if (!slider_p.max_value.isProvided()) - { - slider_p.max_value = p.max_value; - } - if (!slider_p.increment.isProvided()) - { - slider_p.increment = p.increment; - } - if (!slider_p.orientation.isProvided()) - { - slider_p.orientation = p.orientation; - } - - slider_p.commit_callback.function = &LLSliderCtrl::onSliderCommit; - slider_p.control_name = p.control_name; - slider_p.mouse_down_callback( p.mouse_down_callback ); - slider_p.mouse_up_callback( p.mouse_up_callback ); - mSlider = LLUICtrlFactory::create (slider_p); - - addChild( mSlider ); - - if( p.show_text() ) - { - LLRect text_rect( text_left, top, getRect().getWidth(), bottom ); - if( p.can_edit_text() ) - { - LLLineEditor::Params line_p(p.value_editor); - if (!line_p.rect.isProvided()) - { - line_p.rect = text_rect; - } - if (!line_p.font.isProvided()) - { - line_p.font = p.font; - } - - line_p.commit_callback.function(&LLSliderCtrl::onEditorCommit); - line_p.prevalidator(&LLTextValidate::validateFloat); - mEditor = LLUICtrlFactory::create(line_p); - - mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this )); - // don't do this, as selecting the entire text is single clicking in some cases - // and double clicking in others - //mEditor->setSelectAllonFocusReceived(true); - addChild(mEditor); - } - else - { - LLTextBox::Params text_p(p.value_text); - if (!text_p.rect.isProvided()) - { - text_p.rect = text_rect; - } - if (!text_p.font.isProvided()) - { - text_p.font = p.font; - } - mTextBox = LLUICtrlFactory::create(text_p); - addChild(mTextBox); - } - } - - updateText(); -} - -LLSliderCtrl::~LLSliderCtrl() -{ - delete mEditorCommitSignal; -} - -// 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 ); - mValue = mSlider->getValueF32(); - updateText(); -} - -bool LLSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - bool res = false; - if (mLabelBox) - { - res = mLabelBox->setTextArg(key, text); - if (res && mLabelFont && mLabelWidth == 0) - { - S32 label_width = mLabelFont->getWidth(mLabelBox->getText()); - LLRect rect = mLabelBox->getRect(); - S32 prev_right = rect.mRight; - rect.mRight = rect.mLeft + label_width; - mLabelBox->setRect(rect); - - S32 delta = rect.mRight - prev_right; - rect = mSlider->getRect(); - S32 left = rect.mLeft + delta; - static LLUICachedControl sliderctrl_spacing ("UISliderctrlSpacing", 0); - left = llclamp(left, 0, rect.mRight - sliderctrl_spacing); - rect.mLeft = left; - mSlider->setRect(rect); - } - } - return res; -} - -void LLSliderCtrl::clear() -{ - setValue(0.0f); - if( mEditor ) - { - mEditor->setText( LLStringUtil::null ); - } - if( mTextBox ) - { - mTextBox->setText( LLStringUtil::null ); - } - -} - -void LLSliderCtrl::updateText() -{ - if( mEditor || mTextBox ) - { - LLLocale locale(LLLocale::USER_LOCALE); - - // Don't display very small negative values as -0.000 - F32 displayed_value = (F32)(floor(getValueF32() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision)); - - std::string format = llformat("%%.%df", mPrecision); - std::string text = llformat(format.c_str(), displayed_value); - if( mEditor ) - { - // Setting editor text here to "" before using actual text is here because if text which - // is set is the same as the one which is actually typed into lineeditor, LLLineEditor::setText() - // will exit at it's beginning, so text for revert on escape won't be saved. (EXT-8536) - mEditor->setText( LLStringUtil::null ); - mEditor->setText( text ); - } - else - { - mTextBox->setText( text ); - } - } -} - -void LLSliderCtrl::updateSliderRect() -{ - S32 right = getRect().getWidth(); - S32 top = getRect().getHeight(); - S32 bottom = 0; - S32 left = 0; - static LLUICachedControl sliderctrl_spacing("UISliderctrlSpacing", 0); - if (mEditor) - { - LLRect editor_rect = mEditor->getRect(); - S32 editor_width = editor_rect.getWidth(); - editor_rect.mRight = right; - editor_rect.mLeft = right - editor_width; - mEditor->setRect(editor_rect); - - right -= editor_width + sliderctrl_spacing; - } - if (mTextBox) - { - right -= mTextBox->getRect().getWidth() + sliderctrl_spacing; - } - if (mLabelBox) - { - left += mLabelBox->getRect().getWidth() + sliderctrl_spacing; - } - - mSlider->setRect(LLRect(left, top,right,bottom)); -} - -// static -void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata ) -{ - LLSliderCtrl* self = dynamic_cast(ctrl->getParent()); - if (!self) - return; - - bool success = false; - F32 val = self->mValue; - F32 saved_val = self->mValue; - - std::string text = self->mEditor->getText(); - if( LLLineEditor::postvalidateFloat( text ) ) - { - LLLocale locale(LLLocale::USER_LOCALE); - val = (F32) atof( text.c_str() ); - if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() ) - { - self->setValue( val ); // set the value temporarily so that the callback can retrieve it. - if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) ) - { - success = true; - } - } - } - - if( success ) - { - self->onCommit(); - if (self->mEditorCommitSignal) - (*(self->mEditorCommitSignal))(self, self->getValueF32()); - } - else - { - if( self->getValueF32() != saved_val ) - { - self->setValue( saved_val ); - } - self->reportInvalidData(); - } - self->updateText(); -} - -// static -void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata ) -{ - LLSliderCtrl* self = dynamic_cast(ctrl->getParent()); - if (!self) - return; - - bool success = false; - F32 saved_val = self->mValue; - F32 new_val = self->mSlider->getValueF32(); - - self->mValue = new_val; // set the value temporarily so that the callback can retrieve it. - if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) ) - { - success = true; - } - - if( success ) - { - self->onCommit(); - } - else - { - if( self->mValue != saved_val ) - { - self->setValue( saved_val ); - } - self->reportInvalidData(); - } - self->updateText(); -} - -void LLSliderCtrl::setEnabled(bool b) -{ - LLView::setEnabled( b ); - - if( mLabelBox ) - { - mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); - } - - mSlider->setEnabled( b ); - - if( mEditor ) - { - mEditor->setEnabled( b ); - } - - if( mTextBox ) - { - mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); - } -} - - -void LLSliderCtrl::setTentative(bool b) -{ - if( mEditor ) - { - mEditor->setTentative(b); - } - LLF32UICtrl::setTentative(b); -} - - -void LLSliderCtrl::onCommit() -{ - setTentative(false); - - if( mEditor ) - { - mEditor->setTentative(false); - } - - setControlValue(getValueF32()); - LLF32UICtrl::onCommit(); -} - -void LLSliderCtrl::setRect(const LLRect& rect) -{ - LLF32UICtrl::setRect(rect); - updateSliderRect(); -} - -//virtual -void LLSliderCtrl::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLF32UICtrl::reshape(width, height, called_from_parent); - updateSliderRect(); -} - -void LLSliderCtrl::setPrecision(S32 precision) -{ - if (precision < 0 || precision > 10) - { - LL_ERRS() << "LLSliderCtrl::setPrecision - precision out of range" << LL_ENDL; - return; - } - - mPrecision = precision; - updateText(); -} - -boost::signals2::connection LLSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ) -{ - return mSlider->setMouseDownCallback( cb ); -} - -boost::signals2::connection LLSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ) -{ - return mSlider->setMouseUpCallback( cb ); -} - -boost::signals2::connection LLSliderCtrl::setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mEditorCommitSignal) mEditorCommitSignal = new commit_signal_t(); - return mEditorCommitSignal->connect(cb); -} -void LLSliderCtrl::onTabInto() -{ - if( mEditor ) - { - mEditor->onTabInto(); - } - LLF32UICtrl::onTabInto(); -} - -void LLSliderCtrl::reportInvalidData() -{ - make_ui_sound("UISndBadKeystroke"); -} - +/** + * @file llsliderctrl.cpp + * @brief LLSliderCtrl base class + * + * $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$ + */ + +#include "linden_common.h" + +#include "llsliderctrl.h" + +#include "llmath.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llkeyboard.h" +#include "lllineeditor.h" +#include "llslider.h" +#include "llstring.h" +#include "lltextbox.h" +#include "llui.h" +#include "lluiconstants.h" +#include "llcontrol.h" +#include "llfocusmgr.h" +#include "llresmgr.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register r("slider"); + +LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p) +: LLF32UICtrl(p), + mLabelBox( NULL ), + mEditor( NULL ), + mTextBox( NULL ), + mFont(p.font), + mShowText(p.show_text), + mCanEditText(p.can_edit_text), + mPrecision(p.decimal_digits), + mTextEnabledColor(p.text_color()), + mTextDisabledColor(p.text_disabled_color()), + mLabelWidth(p.label_width), + mEditorCommitSignal(NULL) +{ + S32 top = getRect().getHeight(); + S32 bottom = 0; + S32 left = 0; + + S32 label_width = p.label_width; + S32 text_width = p.text_width; + + // Label + if( !p.label().empty() ) + { + if (!p.label_width.isProvided()) + { + label_width = p.font()->getWidth(p.label); + } + LLRect label_rect( left, top, label_width, bottom ); + LLTextBox::Params params(p.slider_label); + if (!params.rect.isProvided()) + { + params.rect = label_rect; + } + if (!params.font.isProvided()) + { + params.font = p.font; + } + params.initial_value(p.label()); + mLabelBox = LLUICtrlFactory::create (params); + addChild(mLabelBox); + mLabelFont = params.font(); + } + + if (p.show_text && !p.text_width.isProvided()) + { + // calculate the size of the text box (log max_value is number of digits - 1 so plus 1) + if ( p.max_value ) + text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 ); + + if ( p.increment < 1.0f ) + text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value + + if ( p.min_value < 0.0f || p.max_value < 0.0f ) + text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign + + // padding to make things look nicer + text_width += 8; + } + + + S32 text_left = getRect().getWidth() - text_width; + static LLUICachedControl sliderctrl_spacing ("UISliderctrlSpacing", 0); + + S32 slider_right = getRect().getWidth(); + if( p.show_text ) + { + slider_right = text_left - sliderctrl_spacing; + } + + S32 slider_left = label_width ? label_width + sliderctrl_spacing : 0; + LLSlider::Params slider_p(p.slider_bar); + slider_p.name("slider_bar"); + if (!slider_p.rect.isProvided()) + { + slider_p.rect = LLRect(slider_left,top,slider_right,bottom); + } + if (!slider_p.initial_value.isProvided()) + { + slider_p.initial_value = p.initial_value().asReal(); + } + if (!slider_p.min_value.isProvided()) + { + slider_p.min_value = p.min_value; + } + if (!slider_p.max_value.isProvided()) + { + slider_p.max_value = p.max_value; + } + if (!slider_p.increment.isProvided()) + { + slider_p.increment = p.increment; + } + if (!slider_p.orientation.isProvided()) + { + slider_p.orientation = p.orientation; + } + + slider_p.commit_callback.function = &LLSliderCtrl::onSliderCommit; + slider_p.control_name = p.control_name; + slider_p.mouse_down_callback( p.mouse_down_callback ); + slider_p.mouse_up_callback( p.mouse_up_callback ); + mSlider = LLUICtrlFactory::create (slider_p); + + addChild( mSlider ); + + if( p.show_text() ) + { + LLRect text_rect( text_left, top, getRect().getWidth(), bottom ); + if( p.can_edit_text() ) + { + LLLineEditor::Params line_p(p.value_editor); + if (!line_p.rect.isProvided()) + { + line_p.rect = text_rect; + } + if (!line_p.font.isProvided()) + { + line_p.font = p.font; + } + + line_p.commit_callback.function(&LLSliderCtrl::onEditorCommit); + line_p.prevalidator(&LLTextValidate::validateFloat); + mEditor = LLUICtrlFactory::create(line_p); + + mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this )); + // don't do this, as selecting the entire text is single clicking in some cases + // and double clicking in others + //mEditor->setSelectAllonFocusReceived(true); + addChild(mEditor); + } + else + { + LLTextBox::Params text_p(p.value_text); + if (!text_p.rect.isProvided()) + { + text_p.rect = text_rect; + } + if (!text_p.font.isProvided()) + { + text_p.font = p.font; + } + mTextBox = LLUICtrlFactory::create(text_p); + addChild(mTextBox); + } + } + + updateText(); +} + +LLSliderCtrl::~LLSliderCtrl() +{ + delete mEditorCommitSignal; +} + +// 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 ); + mValue = mSlider->getValueF32(); + updateText(); +} + +bool LLSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + bool res = false; + if (mLabelBox) + { + res = mLabelBox->setTextArg(key, text); + if (res && mLabelFont && mLabelWidth == 0) + { + S32 label_width = mLabelFont->getWidth(mLabelBox->getText()); + LLRect rect = mLabelBox->getRect(); + S32 prev_right = rect.mRight; + rect.mRight = rect.mLeft + label_width; + mLabelBox->setRect(rect); + + S32 delta = rect.mRight - prev_right; + rect = mSlider->getRect(); + S32 left = rect.mLeft + delta; + static LLUICachedControl sliderctrl_spacing ("UISliderctrlSpacing", 0); + left = llclamp(left, 0, rect.mRight - sliderctrl_spacing); + rect.mLeft = left; + mSlider->setRect(rect); + } + } + return res; +} + +void LLSliderCtrl::clear() +{ + setValue(0.0f); + if( mEditor ) + { + mEditor->setText( LLStringUtil::null ); + } + if( mTextBox ) + { + mTextBox->setText( LLStringUtil::null ); + } + +} + +void LLSliderCtrl::updateText() +{ + if( mEditor || mTextBox ) + { + LLLocale locale(LLLocale::USER_LOCALE); + + // Don't display very small negative values as -0.000 + F32 displayed_value = (F32)(floor(getValueF32() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision)); + + std::string format = llformat("%%.%df", mPrecision); + std::string text = llformat(format.c_str(), displayed_value); + if( mEditor ) + { + // Setting editor text here to "" before using actual text is here because if text which + // is set is the same as the one which is actually typed into lineeditor, LLLineEditor::setText() + // will exit at it's beginning, so text for revert on escape won't be saved. (EXT-8536) + mEditor->setText( LLStringUtil::null ); + mEditor->setText( text ); + } + else + { + mTextBox->setText( text ); + } + } +} + +void LLSliderCtrl::updateSliderRect() +{ + S32 right = getRect().getWidth(); + S32 top = getRect().getHeight(); + S32 bottom = 0; + S32 left = 0; + static LLUICachedControl sliderctrl_spacing("UISliderctrlSpacing", 0); + if (mEditor) + { + LLRect editor_rect = mEditor->getRect(); + S32 editor_width = editor_rect.getWidth(); + editor_rect.mRight = right; + editor_rect.mLeft = right - editor_width; + mEditor->setRect(editor_rect); + + right -= editor_width + sliderctrl_spacing; + } + if (mTextBox) + { + right -= mTextBox->getRect().getWidth() + sliderctrl_spacing; + } + if (mLabelBox) + { + left += mLabelBox->getRect().getWidth() + sliderctrl_spacing; + } + + mSlider->setRect(LLRect(left, top,right,bottom)); +} + +// static +void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata ) +{ + LLSliderCtrl* self = dynamic_cast(ctrl->getParent()); + if (!self) + return; + + bool success = false; + F32 val = self->mValue; + F32 saved_val = self->mValue; + + std::string text = self->mEditor->getText(); + if( LLLineEditor::postvalidateFloat( text ) ) + { + LLLocale locale(LLLocale::USER_LOCALE); + val = (F32) atof( text.c_str() ); + if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() ) + { + self->setValue( val ); // set the value temporarily so that the callback can retrieve it. + if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) ) + { + success = true; + } + } + } + + if( success ) + { + self->onCommit(); + if (self->mEditorCommitSignal) + (*(self->mEditorCommitSignal))(self, self->getValueF32()); + } + else + { + if( self->getValueF32() != saved_val ) + { + self->setValue( saved_val ); + } + self->reportInvalidData(); + } + self->updateText(); +} + +// static +void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata ) +{ + LLSliderCtrl* self = dynamic_cast(ctrl->getParent()); + if (!self) + return; + + bool success = false; + F32 saved_val = self->mValue; + F32 new_val = self->mSlider->getValueF32(); + + self->mValue = new_val; // set the value temporarily so that the callback can retrieve it. + if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) ) + { + success = true; + } + + if( success ) + { + self->onCommit(); + } + else + { + if( self->mValue != saved_val ) + { + self->setValue( saved_val ); + } + self->reportInvalidData(); + } + self->updateText(); +} + +void LLSliderCtrl::setEnabled(bool b) +{ + LLView::setEnabled( b ); + + if( mLabelBox ) + { + mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); + } + + mSlider->setEnabled( b ); + + if( mEditor ) + { + mEditor->setEnabled( b ); + } + + if( mTextBox ) + { + mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() ); + } +} + + +void LLSliderCtrl::setTentative(bool b) +{ + if( mEditor ) + { + mEditor->setTentative(b); + } + LLF32UICtrl::setTentative(b); +} + + +void LLSliderCtrl::onCommit() +{ + setTentative(false); + + if( mEditor ) + { + mEditor->setTentative(false); + } + + setControlValue(getValueF32()); + LLF32UICtrl::onCommit(); +} + +void LLSliderCtrl::setRect(const LLRect& rect) +{ + LLF32UICtrl::setRect(rect); + updateSliderRect(); +} + +//virtual +void LLSliderCtrl::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLF32UICtrl::reshape(width, height, called_from_parent); + updateSliderRect(); +} + +void LLSliderCtrl::setPrecision(S32 precision) +{ + if (precision < 0 || precision > 10) + { + LL_ERRS() << "LLSliderCtrl::setPrecision - precision out of range" << LL_ENDL; + return; + } + + mPrecision = precision; + updateText(); +} + +boost::signals2::connection LLSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ) +{ + return mSlider->setMouseDownCallback( cb ); +} + +boost::signals2::connection LLSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ) +{ + return mSlider->setMouseUpCallback( cb ); +} + +boost::signals2::connection LLSliderCtrl::setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mEditorCommitSignal) mEditorCommitSignal = new commit_signal_t(); + return mEditorCommitSignal->connect(cb); +} +void LLSliderCtrl::onTabInto() +{ + if( mEditor ) + { + mEditor->onTabInto(); + } + LLF32UICtrl::onTabInto(); +} + +void LLSliderCtrl::reportInvalidData() +{ + make_ui_sound("UISndBadKeystroke"); +} + diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h index 993cea4d2f..a55e3bf6e9 100644 --- a/indra/llui/llsliderctrl.h +++ b/indra/llui/llsliderctrl.h @@ -1,176 +1,176 @@ -/** - * @file llsliderctrl.h - * @brief Decorated wrapper for a LLSlider. - * - * $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_LLSLIDERCTRL_H -#define LL_LLSLIDERCTRL_H - -#include "lluictrl.h" -#include "v4color.h" -#include "llslider.h" -#include "lltextbox.h" -#include "llrect.h" -#include "lllineeditor.h" - - -class LLSliderCtrl: public LLF32UICtrl, public ll::ui::SearchableControl -{ -public: - struct Params : public LLInitParam::Block - { - Optional orientation; - Optional label_width; - Optional text_width; - Optional show_text; - Optional can_edit_text; - Optional is_volume_slider; - Optional decimal_digits; - - Optional text_color, - text_disabled_color; - - Optional mouse_down_callback, - mouse_up_callback; - - Optional slider_bar; - Optional value_editor; - Optional value_text; - Optional slider_label; - - Params() - : text_width("text_width"), - label_width("label_width"), - show_text("show_text"), - can_edit_text("can_edit_text"), - is_volume_slider("volume"), - decimal_digits("decimal_digits", 3), - text_color("text_color"), - text_disabled_color("text_disabled_color"), - slider_bar("slider_bar"), - value_editor("value_editor"), - value_text("value_text"), - slider_label("slider_label"), - mouse_down_callback("mouse_down_callback"), - mouse_up_callback("mouse_up_callback"), - orientation("orientation", std::string ("horizontal")) - {} - }; -protected: - LLSliderCtrl(const Params&); - friend class LLUICtrlFactory; -public: - virtual ~LLSliderCtrl(); - - /*virtual*/ F32 getValueF32() const { return mSlider->getValueF32(); } - void setValue(F32 v, bool from_event = false); - - /*virtual*/ void setValue(const LLSD& value) { setValue((F32)value.asReal(), true); } - /*virtual*/ LLSD getValue() const { return LLSD(getValueF32()); } - /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - - bool isMouseHeldDown() const { return mSlider->hasMouseCapture(); } - - virtual void setPrecision(S32 precision); - - /*virtual*/ void setEnabled( bool b ); - /*virtual*/ void clear(); - - /*virtual*/ void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } - /*virtual*/ void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } - /*virtual*/ void setMinValue(F32 min_value) { mSlider->setMinValue(min_value); updateText(); } - /*virtual*/ void setMaxValue(F32 max_value) { mSlider->setMaxValue(max_value); updateText(); } - /*virtual*/ void setIncrement(F32 increment) { mSlider->setIncrement(increment);} - - F32 getMinValue() const { return mSlider->getMinValue(); } - F32 getMaxValue() const { return mSlider->getMaxValue(); } - - void setLabel(const LLStringExplicit& label) { if (mLabelBox) mLabelBox->setText(label); } - void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; } - void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; } - - boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb ); - - /*virtual*/ void onTabInto(); - - /*virtual*/ void setTentative(bool b); // marks value as tentative - /*virtual*/ void onCommit(); // mark not tentative, then commit - - /*virtual*/ void setControlName(const std::string& control_name, LLView* context) - { - LLUICtrl::setControlName(control_name, context); - mSlider->setControlName(control_name, context); - } - - /*virtual*/ void setRect(const LLRect& rect); - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - - static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata); - - static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata); - static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); - static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); - -protected: - virtual std::string _getSearchText() const - { - std::string strLabel; - if( mLabelBox ) - strLabel = mLabelBox->getLabel(); - return strLabel + getToolTip(); - } - virtual void onSetHighlight() const // When highlight, really do highlight the label - { - if( mLabelBox ) - mLabelBox->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() ); - } -private: - void updateText(); - void updateSliderRect(); - void reportInvalidData(); - - const LLFontGL* mFont; - const LLFontGL* mLabelFont; - bool mShowText; - bool mCanEditText; - - S32 mPrecision; - LLTextBox* mLabelBox; - S32 mLabelWidth; - - F32 mValue; - LLSlider* mSlider; - class LLLineEditor* mEditor; - LLTextBox* mTextBox; - - LLUIColor mTextEnabledColor; - LLUIColor mTextDisabledColor; - - commit_signal_t* mEditorCommitSignal; -}; - -#endif // LL_LLSLIDERCTRL_H - +/** + * @file llsliderctrl.h + * @brief Decorated wrapper for a LLSlider. + * + * $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_LLSLIDERCTRL_H +#define LL_LLSLIDERCTRL_H + +#include "lluictrl.h" +#include "v4color.h" +#include "llslider.h" +#include "lltextbox.h" +#include "llrect.h" +#include "lllineeditor.h" + + +class LLSliderCtrl: public LLF32UICtrl, public ll::ui::SearchableControl +{ +public: + struct Params : public LLInitParam::Block + { + Optional orientation; + Optional label_width; + Optional text_width; + Optional show_text; + Optional can_edit_text; + Optional is_volume_slider; + Optional decimal_digits; + + Optional text_color, + text_disabled_color; + + Optional mouse_down_callback, + mouse_up_callback; + + Optional slider_bar; + Optional value_editor; + Optional value_text; + Optional slider_label; + + Params() + : text_width("text_width"), + label_width("label_width"), + show_text("show_text"), + can_edit_text("can_edit_text"), + is_volume_slider("volume"), + decimal_digits("decimal_digits", 3), + text_color("text_color"), + text_disabled_color("text_disabled_color"), + slider_bar("slider_bar"), + value_editor("value_editor"), + value_text("value_text"), + slider_label("slider_label"), + mouse_down_callback("mouse_down_callback"), + mouse_up_callback("mouse_up_callback"), + orientation("orientation", std::string ("horizontal")) + {} + }; +protected: + LLSliderCtrl(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLSliderCtrl(); + + /*virtual*/ F32 getValueF32() const { return mSlider->getValueF32(); } + void setValue(F32 v, bool from_event = false); + + /*virtual*/ void setValue(const LLSD& value) { setValue((F32)value.asReal(), true); } + /*virtual*/ LLSD getValue() const { return LLSD(getValueF32()); } + /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text ); + + bool isMouseHeldDown() const { return mSlider->hasMouseCapture(); } + + virtual void setPrecision(S32 precision); + + /*virtual*/ void setEnabled( bool b ); + /*virtual*/ void clear(); + + /*virtual*/ void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); } + /*virtual*/ void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); } + /*virtual*/ void setMinValue(F32 min_value) { mSlider->setMinValue(min_value); updateText(); } + /*virtual*/ void setMaxValue(F32 max_value) { mSlider->setMaxValue(max_value); updateText(); } + /*virtual*/ void setIncrement(F32 increment) { mSlider->setIncrement(increment);} + + F32 getMinValue() const { return mSlider->getMinValue(); } + F32 getMaxValue() const { return mSlider->getMaxValue(); } + + void setLabel(const LLStringExplicit& label) { if (mLabelBox) mLabelBox->setText(label); } + void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; } + void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; } + + boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb ); + + /*virtual*/ void onTabInto(); + + /*virtual*/ void setTentative(bool b); // marks value as tentative + /*virtual*/ void onCommit(); // mark not tentative, then commit + + /*virtual*/ void setControlName(const std::string& control_name, LLView* context) + { + LLUICtrl::setControlName(control_name, context); + mSlider->setControlName(control_name, context); + } + + /*virtual*/ void setRect(const LLRect& rect); + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + + static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata); + + static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata); + static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); + static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); + +protected: + virtual std::string _getSearchText() const + { + std::string strLabel; + if( mLabelBox ) + strLabel = mLabelBox->getLabel(); + return strLabel + getToolTip(); + } + virtual void onSetHighlight() const // When highlight, really do highlight the label + { + if( mLabelBox ) + mLabelBox->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() ); + } +private: + void updateText(); + void updateSliderRect(); + void reportInvalidData(); + + const LLFontGL* mFont; + const LLFontGL* mLabelFont; + bool mShowText; + bool mCanEditText; + + S32 mPrecision; + LLTextBox* mLabelBox; + S32 mLabelWidth; + + F32 mValue; + LLSlider* mSlider; + class LLLineEditor* mEditor; + LLTextBox* mTextBox; + + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; + + commit_signal_t* mEditorCommitSignal; +}; + +#endif // LL_LLSLIDERCTRL_H + diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index f361877251..7d41d80334 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -1,504 +1,504 @@ -/** - * @file llspinctrl.cpp - * @brief LLSpinCtrl base class - * - * $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 "linden_common.h" - -#include "llspinctrl.h" - -#include "llgl.h" -#include "llui.h" -#include "lluiconstants.h" - -#include "llstring.h" -#include "llfontgl.h" -#include "lllineeditor.h" -#include "llbutton.h" -#include "lltextbox.h" -#include "llkeyboard.h" -#include "llmath.h" -#include "llcontrol.h" -#include "llfocusmgr.h" -#include "llresmgr.h" -#include "lluictrlfactory.h" - -const U32 MAX_STRING_LENGTH = 255; - -static LLDefaultChildRegistry::Register r2("spinner"); - -LLSpinCtrl::Params::Params() -: label_width("label_width"), - decimal_digits("decimal_digits"), - allow_text_entry("allow_text_entry", true), - allow_digits_only("allow_digits_only", false), - label_wrap("label_wrap", false), - text_enabled_color("text_enabled_color"), - text_disabled_color("text_disabled_color"), - up_button("up_button"), - down_button("down_button") -{} - -LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) -: LLF32UICtrl(p), - mLabelBox(NULL), - mbHasBeenSet( false ), - mPrecision(p.decimal_digits), - mTextEnabledColor(p.text_enabled_color()), - mTextDisabledColor(p.text_disabled_color()) -{ - static LLUICachedControl spinctrl_spacing ("UISpinctrlSpacing", 0); - static LLUICachedControl spinctrl_btn_width ("UISpinctrlBtnWidth", 0); - static LLUICachedControl spinctrl_btn_height ("UISpinctrlBtnHeight", 0); - S32 centered_top = getRect().getHeight(); - S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height; - S32 btn_left = 0; - // reserve space for spinner - S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40)); - - // Label - if( !p.label().empty() ) - { - LLRect label_rect( 0, centered_top, label_width, centered_bottom ); - LLTextBox::Params params; - params.wrap(p.label_wrap); - params.name("SpinCtrl Label"); - params.rect(label_rect); - params.initial_value(p.label()); - if (p.font.isProvided()) - { - params.font(p.font); - } - mLabelBox = LLUICtrlFactory::create (params); - addChild(mLabelBox); - - btn_left += label_rect.mRight + spinctrl_spacing; - } - - S32 btn_right = btn_left + spinctrl_btn_width; - - // Spin buttons - LLButton::Params up_button_params(p.up_button); - up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height); - // Click callback starts within the button and ends within the button, - // but LLSpinCtrl handles the action continuosly so subsribers needs to - // be informed about click ending even if outside view, use 'up' instead - up_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); - up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); - up_button_params.commit_on_capture_lost = true; - - mUpBtn = LLUICtrlFactory::create(up_button_params); - addChild(mUpBtn); - - LLButton::Params down_button_params(p.down_button); - down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height); - down_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); - down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); - down_button_params.commit_on_capture_lost = true; - mDownBtn = LLUICtrlFactory::create(down_button_params); - addChild(mDownBtn); - - LLRect editor_rect( btn_right + 1, centered_top, getRect().getWidth(), centered_bottom ); - LLLineEditor::Params params; - params.name("SpinCtrl Editor"); - params.rect(editor_rect); - if (p.font.isProvided()) - { - params.font(p.font); - } - params.max_length.bytes(MAX_STRING_LENGTH); - params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2))); - - //*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit - - params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); - mEditor = LLUICtrlFactory::create (params); - mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onEditorGainFocus, _1, this )); - mEditor->setFocusLostCallback( boost::bind(&LLSpinCtrl::onEditorLostFocus, _1, this )); - if (p.allow_digits_only) - { - mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); - } - //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 - //mEditor->setSelectAllonFocusReceived(true); - mEditor->setSelectAllonCommit(false); - addChild(mEditor); - - updateEditor(); - setUseBoundingRect( true ); -} - -F32 clamp_precision(F32 value, S32 decimal_precision) -{ - // pow() isn't perfect - - F64 clamped_value = value; - for (S32 i = 0; i < decimal_precision; i++) - clamped_value *= 10.0; - - clamped_value = ll_round(clamped_value); - - for (S32 i = 0; i < decimal_precision; i++) - clamped_value /= 10.0; - - return (F32)clamped_value; -} - - -void LLSpinCtrl::onUpBtn( const LLSD& data ) -{ - if( getEnabled() ) - { - std::string text = mEditor->getText(); - if( LLLineEditor::postvalidateFloat( text ) ) - { - - LLLocale locale(LLLocale::USER_LOCALE); - F32 cur_val = (F32) atof(text.c_str()); - - // use getValue()/setValue() to force reload from/to control - F32 val = cur_val + mIncrement; - val = clamp_precision(val, mPrecision); - val = llmin( val, mMaxValue ); - if (val < mMinValue) val = mMinValue; - if (val > mMaxValue) val = mMaxValue; - - F32 saved_val = (F32)getValue().asReal(); - setValue(val); - if( mValidateSignal && !(*mValidateSignal)( this, val ) ) - { - setValue( saved_val ); - reportInvalidData(); - updateEditor(); - return; - } - - updateEditor(); - onCommit(); - } - } -} - -void LLSpinCtrl::onDownBtn( const LLSD& data ) -{ - if( getEnabled() ) - { - std::string text = mEditor->getText(); - if( LLLineEditor::postvalidateFloat( text ) ) - { - - LLLocale locale(LLLocale::USER_LOCALE); - F32 cur_val = (F32) atof(text.c_str()); - - F32 val = cur_val - mIncrement; - val = clamp_precision(val, mPrecision); - val = llmax( val, mMinValue ); - - if (val < mMinValue) val = mMinValue; - if (val > mMaxValue) val = mMaxValue; - - F32 saved_val = (F32)getValue().asReal(); - setValue(val); - if( mValidateSignal && !(*mValidateSignal)( this, val ) ) - { - setValue( saved_val ); - reportInvalidData(); - updateEditor(); - return; - } - - updateEditor(); - onCommit(); - } - } -} - -// static -void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) -{ - LLSpinCtrl* self = (LLSpinCtrl*) userdata; - llassert( caller == self->mEditor ); - - self->onFocusReceived(); -} - -// static -void LLSpinCtrl::onEditorLostFocus( LLFocusableElement* caller, void *userdata ) -{ - LLSpinCtrl* self = (LLSpinCtrl*) userdata; - llassert( caller == self->mEditor ); - - self->onFocusLost(); - - std::string text = self->mEditor->getText(); - - LLLocale locale(LLLocale::USER_LOCALE); - F32 val = (F32)atof(text.c_str()); - - F32 saved_val = self->getValueF32(); - if (saved_val != val && !self->mEditor->isDirty()) - { - // Editor was focused when value update arrived, string - // in editor is different from one in spin control. - // Since editor is not dirty, it won't commit, so either - // attempt to commit value from editor or revert to a more - // recent value from spin control - self->updateEditor(); - } -} - -void LLSpinCtrl::setValue(const LLSD& value ) -{ - F32 v = (F32)value.asReal(); - if (getValueF32() != v || !mbHasBeenSet) - { - mbHasBeenSet = true; - LLF32UICtrl::setValue(value); - - if (!mEditor->hasFocus()) - { - updateEditor(); - } - } -} - -//no matter if Editor has the focus, update the value -void LLSpinCtrl::forceSetValue(const LLSD& value ) -{ - F32 v = (F32)value.asReal(); - if (getValueF32() != v || !mbHasBeenSet) - { - mbHasBeenSet = true; - LLF32UICtrl::setValue(value); - - updateEditor(); - mEditor->resetScrollPosition(); - } -} - -void LLSpinCtrl::clear() -{ - setValue(mMinValue); - mEditor->clear(); - mbHasBeenSet = false; -} - -void LLSpinCtrl::updateLabelColor() -{ - if( mLabelBox ) - { - mLabelBox->setColor( getEnabled() ? mTextEnabledColor.get() : mTextDisabledColor.get() ); - } -} - -void LLSpinCtrl::updateEditor() -{ - LLLocale locale(LLLocale::USER_LOCALE); - - // Don't display very small negative valu es as -0.000 - F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision); - -// if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 ) -// { -// displayed_value = 0.f; -// } - - std::string format = llformat("%%.%df", mPrecision); - std::string text = llformat(format.c_str(), displayed_value); - mEditor->setText( text ); -} - -void LLSpinCtrl::onEditorCommit( const LLSD& data ) -{ - bool success = false; - - if( mEditor->evaluateFloat() ) - { - std::string text = mEditor->getText(); - - LLLocale locale(LLLocale::USER_LOCALE); - F32 val = (F32) atof(text.c_str()); - - if (val < mMinValue) val = mMinValue; - if (val > mMaxValue) val = mMaxValue; - - F32 saved_val = getValueF32(); - setValue(val); - if( !mValidateSignal || (*mValidateSignal)( this, val ) ) - { - success = true; - onCommit(); - } - else - { - setValue(saved_val); - } - } - updateEditor(); - - if( success ) - { - // We commited and clamped value - // try to display as much as possible - mEditor->resetScrollPosition(); - } - else - { - reportInvalidData(); - } -} - - -void LLSpinCtrl::forceEditorCommit() -{ - onEditorCommit( LLSD() ); -} - - -void LLSpinCtrl::setFocus(bool b) -{ - LLUICtrl::setFocus( b ); - mEditor->setFocus( b ); -} - -void LLSpinCtrl::setEnabled(bool b) -{ - LLView::setEnabled( b ); - mEditor->setEnabled( b ); - updateLabelColor(); -} - - -void LLSpinCtrl::setTentative(bool b) -{ - mEditor->setTentative(b); - LLUICtrl::setTentative(b); -} - - -bool LLSpinCtrl::isMouseHeldDown() const -{ - return - mDownBtn->hasMouseCapture() - || mUpBtn->hasMouseCapture(); -} - -void LLSpinCtrl::onCommit() -{ - setTentative(false); - setControlValue(getValueF32()); - LLF32UICtrl::onCommit(); -} - - -void LLSpinCtrl::setPrecision(S32 precision) -{ - if (precision < 0 || precision > 10) - { - LL_ERRS() << "LLSpinCtrl::setPrecision - precision out of range" << LL_ENDL; - return; - } - - mPrecision = precision; - updateEditor(); -} - -void LLSpinCtrl::setLabel(const LLStringExplicit& label) -{ - if (mLabelBox) - { - mLabelBox->setText(label); - } - else - { - LL_WARNS() << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << LL_ENDL; - } - updateLabelColor(); -} - -void LLSpinCtrl::setAllowEdit(bool allow_edit) -{ - mEditor->setEnabled(allow_edit); - mAllowEdit = allow_edit; -} - -void LLSpinCtrl::onTabInto() -{ - mEditor->onTabInto(); - LLF32UICtrl::onTabInto(); -} - - -void LLSpinCtrl::reportInvalidData() -{ - make_ui_sound("UISndBadKeystroke"); -} - -bool LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if( clicks > 0 ) - { - while( clicks-- ) - { - onDownBtn(getValue()); - } - } - else - while( clicks++ ) - { - onUpBtn(getValue()); - } - - return true; -} - -bool LLSpinCtrl::handleKeyHere(KEY key, MASK mask) -{ - if (mEditor->hasFocus()) - { - if(key == KEY_ESCAPE) - { - // text editors don't support revert normally (due to user confusion) - // but not allowing revert on a spinner seems dangerous - updateEditor(); - mEditor->resetScrollPosition(); - mEditor->setFocus(false); - return true; - } - if(key == KEY_UP) - { - onUpBtn(getValue()); - return true; - } - if(key == KEY_DOWN) - { - onDownBtn(getValue()); - return true; - } - } - return false; -} - +/** + * @file llspinctrl.cpp + * @brief LLSpinCtrl base class + * + * $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 "linden_common.h" + +#include "llspinctrl.h" + +#include "llgl.h" +#include "llui.h" +#include "lluiconstants.h" + +#include "llstring.h" +#include "llfontgl.h" +#include "lllineeditor.h" +#include "llbutton.h" +#include "lltextbox.h" +#include "llkeyboard.h" +#include "llmath.h" +#include "llcontrol.h" +#include "llfocusmgr.h" +#include "llresmgr.h" +#include "lluictrlfactory.h" + +const U32 MAX_STRING_LENGTH = 255; + +static LLDefaultChildRegistry::Register r2("spinner"); + +LLSpinCtrl::Params::Params() +: label_width("label_width"), + decimal_digits("decimal_digits"), + allow_text_entry("allow_text_entry", true), + allow_digits_only("allow_digits_only", false), + label_wrap("label_wrap", false), + text_enabled_color("text_enabled_color"), + text_disabled_color("text_disabled_color"), + up_button("up_button"), + down_button("down_button") +{} + +LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) +: LLF32UICtrl(p), + mLabelBox(NULL), + mbHasBeenSet( false ), + mPrecision(p.decimal_digits), + mTextEnabledColor(p.text_enabled_color()), + mTextDisabledColor(p.text_disabled_color()) +{ + static LLUICachedControl spinctrl_spacing ("UISpinctrlSpacing", 0); + static LLUICachedControl spinctrl_btn_width ("UISpinctrlBtnWidth", 0); + static LLUICachedControl spinctrl_btn_height ("UISpinctrlBtnHeight", 0); + S32 centered_top = getRect().getHeight(); + S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height; + S32 btn_left = 0; + // reserve space for spinner + S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40)); + + // Label + if( !p.label().empty() ) + { + LLRect label_rect( 0, centered_top, label_width, centered_bottom ); + LLTextBox::Params params; + params.wrap(p.label_wrap); + params.name("SpinCtrl Label"); + params.rect(label_rect); + params.initial_value(p.label()); + if (p.font.isProvided()) + { + params.font(p.font); + } + mLabelBox = LLUICtrlFactory::create (params); + addChild(mLabelBox); + + btn_left += label_rect.mRight + spinctrl_spacing; + } + + S32 btn_right = btn_left + spinctrl_btn_width; + + // Spin buttons + LLButton::Params up_button_params(p.up_button); + up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height); + // Click callback starts within the button and ends within the button, + // but LLSpinCtrl handles the action continuosly so subsribers needs to + // be informed about click ending even if outside view, use 'up' instead + up_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); + up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); + up_button_params.commit_on_capture_lost = true; + + mUpBtn = LLUICtrlFactory::create(up_button_params); + addChild(mUpBtn); + + LLButton::Params down_button_params(p.down_button); + down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height); + down_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); + down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); + down_button_params.commit_on_capture_lost = true; + mDownBtn = LLUICtrlFactory::create(down_button_params); + addChild(mDownBtn); + + LLRect editor_rect( btn_right + 1, centered_top, getRect().getWidth(), centered_bottom ); + LLLineEditor::Params params; + params.name("SpinCtrl Editor"); + params.rect(editor_rect); + if (p.font.isProvided()) + { + params.font(p.font); + } + params.max_length.bytes(MAX_STRING_LENGTH); + params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2))); + + //*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit + + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + mEditor = LLUICtrlFactory::create (params); + mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onEditorGainFocus, _1, this )); + mEditor->setFocusLostCallback( boost::bind(&LLSpinCtrl::onEditorLostFocus, _1, this )); + if (p.allow_digits_only) + { + mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); + } + //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 + //mEditor->setSelectAllonFocusReceived(true); + mEditor->setSelectAllonCommit(false); + addChild(mEditor); + + updateEditor(); + setUseBoundingRect( true ); +} + +F32 clamp_precision(F32 value, S32 decimal_precision) +{ + // pow() isn't perfect + + F64 clamped_value = value; + for (S32 i = 0; i < decimal_precision; i++) + clamped_value *= 10.0; + + clamped_value = ll_round(clamped_value); + + for (S32 i = 0; i < decimal_precision; i++) + clamped_value /= 10.0; + + return (F32)clamped_value; +} + + +void LLSpinCtrl::onUpBtn( const LLSD& data ) +{ + if( getEnabled() ) + { + std::string text = mEditor->getText(); + if( LLLineEditor::postvalidateFloat( text ) ) + { + + LLLocale locale(LLLocale::USER_LOCALE); + F32 cur_val = (F32) atof(text.c_str()); + + // use getValue()/setValue() to force reload from/to control + F32 val = cur_val + mIncrement; + val = clamp_precision(val, mPrecision); + val = llmin( val, mMaxValue ); + if (val < mMinValue) val = mMinValue; + if (val > mMaxValue) val = mMaxValue; + + F32 saved_val = (F32)getValue().asReal(); + setValue(val); + if( mValidateSignal && !(*mValidateSignal)( this, val ) ) + { + setValue( saved_val ); + reportInvalidData(); + updateEditor(); + return; + } + + updateEditor(); + onCommit(); + } + } +} + +void LLSpinCtrl::onDownBtn( const LLSD& data ) +{ + if( getEnabled() ) + { + std::string text = mEditor->getText(); + if( LLLineEditor::postvalidateFloat( text ) ) + { + + LLLocale locale(LLLocale::USER_LOCALE); + F32 cur_val = (F32) atof(text.c_str()); + + F32 val = cur_val - mIncrement; + val = clamp_precision(val, mPrecision); + val = llmax( val, mMinValue ); + + if (val < mMinValue) val = mMinValue; + if (val > mMaxValue) val = mMaxValue; + + F32 saved_val = (F32)getValue().asReal(); + setValue(val); + if( mValidateSignal && !(*mValidateSignal)( this, val ) ) + { + setValue( saved_val ); + reportInvalidData(); + updateEditor(); + return; + } + + updateEditor(); + onCommit(); + } + } +} + +// static +void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) +{ + LLSpinCtrl* self = (LLSpinCtrl*) userdata; + llassert( caller == self->mEditor ); + + self->onFocusReceived(); +} + +// static +void LLSpinCtrl::onEditorLostFocus( LLFocusableElement* caller, void *userdata ) +{ + LLSpinCtrl* self = (LLSpinCtrl*) userdata; + llassert( caller == self->mEditor ); + + self->onFocusLost(); + + std::string text = self->mEditor->getText(); + + LLLocale locale(LLLocale::USER_LOCALE); + F32 val = (F32)atof(text.c_str()); + + F32 saved_val = self->getValueF32(); + if (saved_val != val && !self->mEditor->isDirty()) + { + // Editor was focused when value update arrived, string + // in editor is different from one in spin control. + // Since editor is not dirty, it won't commit, so either + // attempt to commit value from editor or revert to a more + // recent value from spin control + self->updateEditor(); + } +} + +void LLSpinCtrl::setValue(const LLSD& value ) +{ + F32 v = (F32)value.asReal(); + if (getValueF32() != v || !mbHasBeenSet) + { + mbHasBeenSet = true; + LLF32UICtrl::setValue(value); + + if (!mEditor->hasFocus()) + { + updateEditor(); + } + } +} + +//no matter if Editor has the focus, update the value +void LLSpinCtrl::forceSetValue(const LLSD& value ) +{ + F32 v = (F32)value.asReal(); + if (getValueF32() != v || !mbHasBeenSet) + { + mbHasBeenSet = true; + LLF32UICtrl::setValue(value); + + updateEditor(); + mEditor->resetScrollPosition(); + } +} + +void LLSpinCtrl::clear() +{ + setValue(mMinValue); + mEditor->clear(); + mbHasBeenSet = false; +} + +void LLSpinCtrl::updateLabelColor() +{ + if( mLabelBox ) + { + mLabelBox->setColor( getEnabled() ? mTextEnabledColor.get() : mTextDisabledColor.get() ); + } +} + +void LLSpinCtrl::updateEditor() +{ + LLLocale locale(LLLocale::USER_LOCALE); + + // Don't display very small negative valu es as -0.000 + F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision); + +// if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 ) +// { +// displayed_value = 0.f; +// } + + std::string format = llformat("%%.%df", mPrecision); + std::string text = llformat(format.c_str(), displayed_value); + mEditor->setText( text ); +} + +void LLSpinCtrl::onEditorCommit( const LLSD& data ) +{ + bool success = false; + + if( mEditor->evaluateFloat() ) + { + std::string text = mEditor->getText(); + + LLLocale locale(LLLocale::USER_LOCALE); + F32 val = (F32) atof(text.c_str()); + + if (val < mMinValue) val = mMinValue; + if (val > mMaxValue) val = mMaxValue; + + F32 saved_val = getValueF32(); + setValue(val); + if( !mValidateSignal || (*mValidateSignal)( this, val ) ) + { + success = true; + onCommit(); + } + else + { + setValue(saved_val); + } + } + updateEditor(); + + if( success ) + { + // We commited and clamped value + // try to display as much as possible + mEditor->resetScrollPosition(); + } + else + { + reportInvalidData(); + } +} + + +void LLSpinCtrl::forceEditorCommit() +{ + onEditorCommit( LLSD() ); +} + + +void LLSpinCtrl::setFocus(bool b) +{ + LLUICtrl::setFocus( b ); + mEditor->setFocus( b ); +} + +void LLSpinCtrl::setEnabled(bool b) +{ + LLView::setEnabled( b ); + mEditor->setEnabled( b ); + updateLabelColor(); +} + + +void LLSpinCtrl::setTentative(bool b) +{ + mEditor->setTentative(b); + LLUICtrl::setTentative(b); +} + + +bool LLSpinCtrl::isMouseHeldDown() const +{ + return + mDownBtn->hasMouseCapture() + || mUpBtn->hasMouseCapture(); +} + +void LLSpinCtrl::onCommit() +{ + setTentative(false); + setControlValue(getValueF32()); + LLF32UICtrl::onCommit(); +} + + +void LLSpinCtrl::setPrecision(S32 precision) +{ + if (precision < 0 || precision > 10) + { + LL_ERRS() << "LLSpinCtrl::setPrecision - precision out of range" << LL_ENDL; + return; + } + + mPrecision = precision; + updateEditor(); +} + +void LLSpinCtrl::setLabel(const LLStringExplicit& label) +{ + if (mLabelBox) + { + mLabelBox->setText(label); + } + else + { + LL_WARNS() << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << LL_ENDL; + } + updateLabelColor(); +} + +void LLSpinCtrl::setAllowEdit(bool allow_edit) +{ + mEditor->setEnabled(allow_edit); + mAllowEdit = allow_edit; +} + +void LLSpinCtrl::onTabInto() +{ + mEditor->onTabInto(); + LLF32UICtrl::onTabInto(); +} + + +void LLSpinCtrl::reportInvalidData() +{ + make_ui_sound("UISndBadKeystroke"); +} + +bool LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if( clicks > 0 ) + { + while( clicks-- ) + { + onDownBtn(getValue()); + } + } + else + while( clicks++ ) + { + onUpBtn(getValue()); + } + + return true; +} + +bool LLSpinCtrl::handleKeyHere(KEY key, MASK mask) +{ + if (mEditor->hasFocus()) + { + if(key == KEY_ESCAPE) + { + // text editors don't support revert normally (due to user confusion) + // but not allowing revert on a spinner seems dangerous + updateEditor(); + mEditor->resetScrollPosition(); + mEditor->setFocus(false); + return true; + } + if(key == KEY_UP) + { + onUpBtn(getValue()); + return true; + } + if(key == KEY_DOWN) + { + onDownBtn(getValue()); + return true; + } + } + return false; +} + diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index 471747f404..75f1830d80 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -1,124 +1,124 @@ -/** - * @file llspinctrl.h - * @brief Typical spinner with "up" and "down" arrow buttons. - * - * $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_LLSPINCTRL_H -#define LL_LLSPINCTRL_H - - -#include "stdtypes.h" -#include "llbutton.h" -#include "llf32uictrl.h" -#include "v4color.h" -#include "llrect.h" - - -class LLSpinCtrl -: public LLF32UICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Optional label_width; - Optional decimal_digits; - Optional allow_text_entry; - Optional allow_digits_only; - Optional label_wrap; - - Optional text_enabled_color; - Optional text_disabled_color; - - Optional up_button; - Optional down_button; - - Params(); - }; -protected: - LLSpinCtrl(const Params&); - friend class LLUICtrlFactory; -public: - virtual ~LLSpinCtrl() {} // Children all cleaned up by default view destructor. - - virtual void forceSetValue(const LLSD& value ) ; - virtual void setValue(const LLSD& value ); - F32 get() const { return getValueF32(); } - void set(F32 value) { setValue(value); mInitialValue = value; } - - bool isMouseHeldDown() const; - - virtual void setEnabled( bool b ); - virtual void setFocus( bool b ); - virtual void clear(); - virtual bool isDirty() const { return( getValueF32() != mInitialValue ); } - virtual void resetDirty() { mInitialValue = getValueF32(); } - - virtual void setPrecision(S32 precision); - - void setLabel(const LLStringExplicit& label); - void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; updateLabelColor(); } - void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; updateLabelColor();} - void setAllowEdit(bool allow_edit); - - virtual void onTabInto(); - - virtual void setTentative(bool b); // marks value as tentative - virtual void onCommit(); // mark not tentative, then commit - - void forceEditorCommit(); // for commit on external button - - virtual bool handleScrollWheel(S32 x,S32 y,S32 clicks); - virtual bool handleKeyHere(KEY key, MASK mask); - - void onEditorCommit(const LLSD& data); - static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); - static void onEditorLostFocus(LLFocusableElement* caller, void *userdata); - static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); - - void onUpBtn(const LLSD& data); - void onDownBtn(const LLSD& data); - - const LLColor4& getEnabledTextColor() const { return mTextEnabledColor.get(); } - const LLColor4& getDisabledTextColor() const { return mTextDisabledColor.get(); } - -private: - void updateLabelColor(); - void updateEditor(); - void reportInvalidData(); - - S32 mPrecision; - class LLTextBox* mLabelBox; - - class LLLineEditor* mEditor; - LLUIColor mTextEnabledColor; - LLUIColor mTextDisabledColor; - - class LLButton* mUpBtn; - class LLButton* mDownBtn; - - bool mbHasBeenSet; - bool mAllowEdit; -}; - -#endif // LL_LLSPINCTRL_H +/** + * @file llspinctrl.h + * @brief Typical spinner with "up" and "down" arrow buttons. + * + * $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_LLSPINCTRL_H +#define LL_LLSPINCTRL_H + + +#include "stdtypes.h" +#include "llbutton.h" +#include "llf32uictrl.h" +#include "v4color.h" +#include "llrect.h" + + +class LLSpinCtrl +: public LLF32UICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional label_width; + Optional decimal_digits; + Optional allow_text_entry; + Optional allow_digits_only; + Optional label_wrap; + + Optional text_enabled_color; + Optional text_disabled_color; + + Optional up_button; + Optional down_button; + + Params(); + }; +protected: + LLSpinCtrl(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLSpinCtrl() {} // Children all cleaned up by default view destructor. + + virtual void forceSetValue(const LLSD& value ) ; + virtual void setValue(const LLSD& value ); + F32 get() const { return getValueF32(); } + void set(F32 value) { setValue(value); mInitialValue = value; } + + bool isMouseHeldDown() const; + + virtual void setEnabled( bool b ); + virtual void setFocus( bool b ); + virtual void clear(); + virtual bool isDirty() const { return( getValueF32() != mInitialValue ); } + virtual void resetDirty() { mInitialValue = getValueF32(); } + + virtual void setPrecision(S32 precision); + + void setLabel(const LLStringExplicit& label); + void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; updateLabelColor(); } + void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; updateLabelColor();} + void setAllowEdit(bool allow_edit); + + virtual void onTabInto(); + + virtual void setTentative(bool b); // marks value as tentative + virtual void onCommit(); // mark not tentative, then commit + + void forceEditorCommit(); // for commit on external button + + virtual bool handleScrollWheel(S32 x,S32 y,S32 clicks); + virtual bool handleKeyHere(KEY key, MASK mask); + + void onEditorCommit(const LLSD& data); + static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); + static void onEditorLostFocus(LLFocusableElement* caller, void *userdata); + static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); + + void onUpBtn(const LLSD& data); + void onDownBtn(const LLSD& data); + + const LLColor4& getEnabledTextColor() const { return mTextEnabledColor.get(); } + const LLColor4& getDisabledTextColor() const { return mTextDisabledColor.get(); } + +private: + void updateLabelColor(); + void updateEditor(); + void reportInvalidData(); + + S32 mPrecision; + class LLTextBox* mLabelBox; + + class LLLineEditor* mEditor; + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; + + class LLButton* mUpBtn; + class LLButton* mDownBtn; + + bool mbHasBeenSet; + bool mAllowEdit; +}; + +#endif // LL_LLSPINCTRL_H diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp index 6e9b19a4de..f125dda916 100644 --- a/indra/llui/llstatbar.cpp +++ b/indra/llui/llstatbar.cpp @@ -1,716 +1,716 @@ -/** - * @file llstatbar.cpp - * @brief A little map of the world with network information - * - * $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 "linden_common.h" - -#include "llstatbar.h" - -#include "llmath.h" -#include "llui.h" -#include "llgl.h" -#include "llfontgl.h" - -#include "lluictrlfactory.h" -#include "lltracerecording.h" -#include "llcriticaldamp.h" -#include "lltooltip.h" -#include "lllocalcliprect.h" -#include -#include "lltrans.h" - -// rate at which to update display of value that is rapidly changing -const F32 MEAN_VALUE_UPDATE_TIME = 1.f / 4.f; -// time between value changes that qualifies as a "rapid change" -const F32Seconds RAPID_CHANGE_THRESHOLD(0.2f); -// maximum number of rapid changes in RAPID_CHANGE_WINDOW before switching over to displaying the mean -// instead of latest value -const S32 MAX_RAPID_CHANGES_PER_SEC = 10; -// period of time over which to measure rapid changes -const F32Seconds RAPID_CHANGE_WINDOW(1.f); - -F32 calc_tick_value(F32 min, F32 max) -{ - F32 range = max - min; - const S32 DIVISORS[] = {6, 8, 10, 4, 5}; - // try storing - S32 best_decimal_digit_count = S32_MAX; - S32 best_divisor = 10; - for (U32 divisor_idx = 0; divisor_idx < LL_ARRAY_SIZE(DIVISORS); divisor_idx++) - { - S32 divisor = DIVISORS[divisor_idx]; - F32 possible_tick_value = range / divisor; - S32 num_whole_digits = llceil(logf(llabs(min + possible_tick_value)) * OO_LN10); - for (S32 digit_count = -(num_whole_digits - 1); digit_count < 6; digit_count++) - { - F32 test_tick_value = min + (possible_tick_value * pow(10.0, digit_count)); - - if (is_approx_equal((F32)(S32)test_tick_value, test_tick_value)) - { - if (digit_count < best_decimal_digit_count) - { - best_decimal_digit_count = digit_count; - best_divisor = divisor; - } - break; - } - } - } - - return is_approx_equal(range, 0.f) ? 0.f : range / best_divisor; -} - -void calc_auto_scale_range(F32& min, F32& max, F32& tick) -{ - min = llmin(0.f, min, max); - max = llmax(0.f, min, max); - - const F32 RANGES[] = {0.f, 1.f, 1.5f, 2.f, 3.f, 5.f, 10.f}; - const F32 TICKS[] = {0.f, 0.25f, 0.5f, 1.f, 1.f, 1.f, 2.f }; - - const S32 num_digits_max = is_approx_equal(llabs(max), 0.f) - ? S32_MIN + 1 - : llceil(logf(llabs(max)) * OO_LN10); - const S32 num_digits_min = is_approx_equal(llabs(min), 0.f) - ? S32_MIN + 1 - : llceil(logf(llabs(min)) * OO_LN10); - - const S32 num_digits = llmax(num_digits_max, num_digits_min); - const F32 power_of_10 = pow(10.0, num_digits - 1); - const F32 starting_max = power_of_10 * ((max < 0.f) ? -1 : 1); - const F32 starting_min = power_of_10 * ((min < 0.f) ? -1 : 1); - - F32 cur_max = starting_max; - F32 cur_min = starting_min; - F32 out_max = max; - F32 out_min = min; - - F32 cur_tick_min = 0.f; - F32 cur_tick_max = 0.f; - - for (S32 range_idx = 0; range_idx < LL_ARRAY_SIZE(RANGES); range_idx++) - { - cur_max = starting_max * RANGES[range_idx]; - cur_min = starting_min * RANGES[range_idx]; - - if (min > 0.f && cur_min <= min) - { - out_min = cur_min; - cur_tick_min = TICKS[range_idx]; - } - if (max < 0.f && cur_max >= max) - { - out_max = cur_max; - cur_tick_max = TICKS[range_idx]; - } - } - - cur_max = starting_max; - cur_min = starting_min; - for (S32 range_idx = LL_ARRAY_SIZE(RANGES) - 1; range_idx >= 0; range_idx--) - { - cur_max = starting_max * RANGES[range_idx]; - cur_min = starting_min * RANGES[range_idx]; - - if (min < 0.f && cur_min <= min) - { - out_min = cur_min; - cur_tick_min = TICKS[range_idx]; - } - if (max > 0.f && cur_max >= max) - { - out_max = cur_max; - cur_tick_max = TICKS[range_idx]; - } - } - - tick = power_of_10 * llmax(cur_tick_min, cur_tick_max); - min = out_min; - max = out_max; -} - -LLStatBar::Params::Params() -: label("label"), - unit_label("unit_label"), - bar_min("bar_min", 0.f), - bar_max("bar_max", 0.f), - tick_spacing("tick_spacing", 0.f), - decimal_digits("decimal_digits", 3), - show_bar("show_bar", false), - show_median("show_median", false), - show_history("show_history", false), - scale_range("scale_range", true), - num_frames("num_frames", 200), - num_frames_short("num_frames_short", 20), - max_height("max_height", 100), - stat("stat"), - orientation("orientation", VERTICAL) -{ - changeDefault(follows.flags, FOLLOWS_TOP | FOLLOWS_LEFT); -} - -/////////////////////////////////////////////////////////////////////////////////// - -LLStatBar::LLStatBar(const Params& p) -: LLView(p), - mLabel(p.label), - mUnitLabel(p.unit_label), - mTargetMinBar(llmin(p.bar_min, p.bar_max)), - mTargetMaxBar(llmax(p.bar_max, p.bar_min)), - mCurMaxBar(p.bar_max), - mCurMinBar(0), - mDecimalDigits(p.decimal_digits), - mNumHistoryFrames(p.num_frames), - mNumShortHistoryFrames(p.num_frames_short), - mMaxHeight(p.max_height), - mDisplayBar(p.show_bar), - mShowMedian(p.show_median), - mDisplayHistory(p.show_history), - mOrientation(p.orientation), - mAutoScaleMax(!p.bar_max.isProvided()), - mAutoScaleMin(!p.bar_min.isProvided()), - mTickSpacing(p.tick_spacing), - mLastDisplayValue(0.f), - mStatType(STAT_NONE) -{ - mFloatingTargetMinBar = mTargetMinBar; - mFloatingTargetMaxBar = mTargetMaxBar; - - mStat.valid = NULL; - // tick value will be automatically calculated later - if (!p.tick_spacing.isProvided() && p.bar_min.isProvided() && p.bar_max.isProvided()) - { - mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar); - } - - setStat(p.stat); -} - -bool LLStatBar::handleHover(S32 x, S32 y, MASK mask) -{ - switch(mStatType) - { - case STAT_COUNT: - LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.countStatp->getDescription()).sticky_rect(calcScreenRect())); - break; - case STAT_EVENT: - LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.eventStatp->getDescription()).sticky_rect(calcScreenRect())); - break; - case STAT_SAMPLE: - LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.sampleStatp->getDescription()).sticky_rect(calcScreenRect())); - break; - default: - break; - } - return true; -} - -bool LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = LLView::handleMouseDown(x, y, mask); - if (!handled) - { - if (mDisplayBar) - { - if (mDisplayHistory || mOrientation == HORIZONTAL) - { - mDisplayBar = false; - mDisplayHistory = false; - } - else - { - mDisplayHistory = true; - } - } - else - { - mDisplayBar = true; - if (mOrientation == HORIZONTAL) - { - mDisplayHistory = true; - } - } - LLView* parent = getParent(); - parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), false); - } - return true; -} - -template -S32 calc_num_rapid_changes(LLTrace::PeriodicRecording& periodic_recording, const T& stat, const F32Seconds time_period) -{ - F32Seconds elapsed_time, - time_since_value_changed; - S32 num_rapid_changes = 0; - const F32Seconds RAPID_CHANGE_THRESHOLD = F32Seconds(0.3f); - F64 last_value = periodic_recording.getPrevRecording(1).getLastValue(stat); - - for (S32 i = 2; i < periodic_recording.getNumRecordedPeriods(); i++) - { - LLTrace::Recording& recording = periodic_recording.getPrevRecording(i); - F64 cur_value = recording.getLastValue(stat); - - if (last_value != cur_value) - { - if (time_since_value_changed < RAPID_CHANGE_THRESHOLD) num_rapid_changes++; - time_since_value_changed = (F32Seconds)0; - } - last_value = cur_value; - - elapsed_time += recording.getDuration(); - if (elapsed_time > time_period) break; - } - - return num_rapid_changes; -} - -void LLStatBar::draw() -{ - LLLocalClipRect _(getLocalRect()); - - LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording(); - LLTrace::Recording& last_frame_recording = frame_recording.getLastRecording(); - - std::string unit_label; - F32 current = 0, - min = 0, - max = 0, - mean = 0, - display_value = 0; - S32 num_frames = mDisplayHistory - ? mNumHistoryFrames - : mNumShortHistoryFrames; - S32 num_rapid_changes = 0; - S32 decimal_digits = mDecimalDigits; - - switch(mStatType) - { - case STAT_COUNT: - { - const LLTrace::StatType& count_stat = *mStat.countStatp; - - unit_label = std::string(count_stat.getUnitLabel()) + "/s"; - current = last_frame_recording.getPerSec(count_stat); - min = frame_recording.getPeriodMinPerSec(count_stat, num_frames); - max = frame_recording.getPeriodMaxPerSec(count_stat, num_frames); - mean = frame_recording.getPeriodMeanPerSec(count_stat, num_frames); - if (mShowMedian) - { - display_value = frame_recording.getPeriodMedianPerSec(count_stat, num_frames); - } - else - { - display_value = mean; - } - } - break; - case STAT_EVENT: - { - const LLTrace::StatType& event_stat = *mStat.eventStatp; - - unit_label = mUnitLabel.empty() ? event_stat.getUnitLabel() : mUnitLabel; - current = last_frame_recording.getLastValue(event_stat); - min = frame_recording.getPeriodMin(event_stat, num_frames); - max = frame_recording.getPeriodMax(event_stat, num_frames); - mean = frame_recording.getPeriodMean(event_stat, num_frames); - display_value = mean; - } - break; - case STAT_SAMPLE: - { - const LLTrace::StatType& sample_stat = *mStat.sampleStatp; - - unit_label = mUnitLabel.empty() ? sample_stat.getUnitLabel() : mUnitLabel; - current = last_frame_recording.getLastValue(sample_stat); - min = frame_recording.getPeriodMin(sample_stat, num_frames); - max = frame_recording.getPeriodMax(sample_stat, num_frames); - mean = frame_recording.getPeriodMean(sample_stat, num_frames); - num_rapid_changes = calc_num_rapid_changes(frame_recording, sample_stat, RAPID_CHANGE_WINDOW); - - if (mShowMedian) - { - display_value = frame_recording.getPeriodMedian(sample_stat, num_frames); - } - else if (num_rapid_changes / RAPID_CHANGE_WINDOW.value() > MAX_RAPID_CHANGES_PER_SEC) - { - display_value = mean; - } - else - { - display_value = current; - // always display current value, don't rate limit - mLastDisplayValue = current; - if (is_approx_equal((F32)(S32)display_value, display_value)) - { - decimal_digits = 0; - } - } - } - break; - default: - break; - } - - LLRect bar_rect; - if (mOrientation == HORIZONTAL) - { - bar_rect.mTop = llmax(5, getRect().getHeight() - 15); - bar_rect.mLeft = 0; - bar_rect.mRight = getRect().getWidth() - 40; - bar_rect.mBottom = llmin(bar_rect.mTop - 5, 0); - } - else // VERTICAL - { - bar_rect.mTop = llmax(5, getRect().getHeight() - 15); - bar_rect.mLeft = 0; - bar_rect.mRight = getRect().getWidth(); - bar_rect.mBottom = llmin(bar_rect.mTop - 5, 20); - } - - mCurMaxBar = LLSmoothInterpolation::lerp(mCurMaxBar, mTargetMaxBar, 0.05f); - mCurMinBar = LLSmoothInterpolation::lerp(mCurMinBar, mTargetMinBar, 0.05f); - - // rate limited updates - if (mLastDisplayValueTimer.getElapsedTimeF32() < MEAN_VALUE_UPDATE_TIME) - { - display_value = mLastDisplayValue; - } - else - { - mLastDisplayValueTimer.reset(); - } - drawLabelAndValue(display_value, unit_label, bar_rect, decimal_digits); - mLastDisplayValue = display_value; - - if (mDisplayBar && mStat.valid) - { - // Draw the tick marks. - LLGLSUIDefault gls_ui; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - F32 value_scale; - if (mCurMaxBar == mCurMinBar) - { - value_scale = 0.f; - } - else - { - value_scale = (mOrientation == HORIZONTAL) - ? (bar_rect.getHeight())/(mCurMaxBar - mCurMinBar) - : (bar_rect.getWidth())/(mCurMaxBar - mCurMinBar); - } - - drawTicks(min, max, value_scale, bar_rect); - - // draw background bar. - gl_rect_2d(bar_rect.mLeft, bar_rect.mTop, bar_rect.mRight, bar_rect.mBottom, LLColor4(0.f, 0.f, 0.f, 0.25f)); - - // draw values - if (!llisnan(display_value) && frame_recording.getNumRecordedPeriods() != 0) - { - // draw min and max - S32 begin = (S32) ((min - mCurMinBar) * value_scale); - - if (begin < 0) - { - begin = 0; - } - - S32 end = (S32) ((max - mCurMinBar) * value_scale); - if (mOrientation == HORIZONTAL) - { - gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 0.25f)); - } - else // VERTICAL - { - gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.f, 0.f, 0.25f)); - } - - F32 span = (mOrientation == HORIZONTAL) - ? (bar_rect.getWidth()) - : (bar_rect.getHeight()); - - if (mDisplayHistory && mStat.valid) - { - const S32 num_values = frame_recording.getNumRecordedPeriods() - 1; - F32 min_value = 0.f, - max_value = 0.f; - - gGL.color4f(1.f, 0.f, 0.f, 1.f); - gGL.begin( LLRender::QUADS ); - const S32 max_frame = llmin(num_frames, num_values); - U32 num_samples = 0; - for (S32 i = 1; i <= max_frame; i++) - { - F32 offset = ((F32)i / (F32)num_frames) * span; - LLTrace::Recording& recording = frame_recording.getPrevRecording(i); - - switch(mStatType) - { - case STAT_COUNT: - min_value = recording.getPerSec(*mStat.countStatp); - max_value = min_value; - num_samples = recording.getSampleCount(*mStat.countStatp); - break; - case STAT_EVENT: - min_value = recording.getMin(*mStat.eventStatp); - max_value = recording.getMax(*mStat.eventStatp); - num_samples = recording.getSampleCount(*mStat.eventStatp); - break; - case STAT_SAMPLE: - min_value = recording.getMin(*mStat.sampleStatp); - max_value = recording.getMax(*mStat.sampleStatp); - num_samples = recording.getSampleCount(*mStat.sampleStatp); - break; - default: - break; - } - - if (!num_samples) continue; - - F32 min = (min_value - mCurMinBar) * value_scale; - F32 max = llmax(min + 1, (max_value - mCurMinBar) * value_scale); - if (mOrientation == HORIZONTAL) - { - gGL.vertex2f((F32)bar_rect.mRight - offset, max); - gGL.vertex2f((F32)bar_rect.mRight - offset, min); - gGL.vertex2f((F32)bar_rect.mRight - offset - 1, min); - gGL.vertex2f((F32)bar_rect.mRight - offset - 1, max); - } - else - { - gGL.vertex2f(min, (F32)bar_rect.mBottom + offset + 1); - gGL.vertex2f(min, (F32)bar_rect.mBottom + offset); - gGL.vertex2f(max, (F32)bar_rect.mBottom + offset); - gGL.vertex2f(max, (F32)bar_rect.mBottom + offset + 1 ); - } - } - gGL.end(); - } - else - { - S32 begin = (S32) ((current - mCurMinBar) * value_scale) - 1; - S32 end = (S32) ((current - mCurMinBar) * value_scale) + 1; - // draw current - if (mOrientation == HORIZONTAL) - { - gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 1.f)); - } - else - { - gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.f, 0.f, 1.f)); - } - } - - // draw mean bar - { - const S32 begin = (S32) ((mean - mCurMinBar) * value_scale) - 1; - const S32 end = (S32) ((mean - mCurMinBar) * value_scale) + 1; - if (mOrientation == HORIZONTAL) - { - gl_rect_2d(bar_rect.mLeft - 2, begin, bar_rect.mRight + 2, end, LLColor4(0.f, 1.f, 0.f, 1.f)); - } - else - { - gl_rect_2d(begin, bar_rect.mTop + 2, end, bar_rect.mBottom - 2, LLColor4(0.f, 1.f, 0.f, 1.f)); - } - } - } - } - - LLView::draw(); -} - -void LLStatBar::setStat(const std::string& stat_name) -{ - using namespace LLTrace; - - if (auto count_stat = StatType::getInstance(stat_name)) - { - mStat.countStatp = count_stat.get(); - mStatType = STAT_COUNT; - } - else if (auto event_stat = StatType::getInstance(stat_name)) - { - mStat.eventStatp = event_stat.get(); - mStatType = STAT_EVENT; - } - else if (auto sample_stat = StatType::getInstance(stat_name)) - { - mStat.sampleStatp = sample_stat.get(); - mStatType = STAT_SAMPLE; - } -} - -void LLStatBar::setRange(F32 bar_min, F32 bar_max) -{ - mTargetMinBar = llmin(bar_min, bar_max); - mTargetMaxBar = llmax(bar_min, bar_max); - mFloatingTargetMinBar = mTargetMinBar; - mFloatingTargetMaxBar = mTargetMaxBar; - mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar); -} - -LLRect LLStatBar::getRequiredRect() -{ - LLRect rect; - - if (mDisplayBar) - { - if (mDisplayHistory) - { - rect.mTop = mMaxHeight; - } - else - { - rect.mTop = 40; - } - } - else - { - rect.mTop = 14; - } - return rect; -} - -void LLStatBar::drawLabelAndValue( F32 value, std::string &label, LLRect &bar_rect, S32 decimal_digits ) -{ - LLFontGL::getFontMonospace()->renderUTF8(mLabel, 0, 0, getRect().getHeight(), LLColor4(1.f, 1.f, 1.f, 1.f), - LLFontGL::LEFT, LLFontGL::TOP); - - std::string value_str = !llisnan(value) - ? llformat("%10.*f %s", decimal_digits, value, label.c_str()) - : LLTrans::getString("na"); - - // Draw the current value. - if (mOrientation == HORIZONTAL) - { - LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(), - LLColor4(1.f, 1.f, 1.f, 1.f), - LLFontGL::RIGHT, LLFontGL::TOP); - } - else - { - LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(), - LLColor4(1.f, 1.f, 1.f, 1.f), - LLFontGL::RIGHT, LLFontGL::TOP); - } -} - -void LLStatBar::drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect ) -{ - if (!llisnan(min) && (mAutoScaleMax || mAutoScaleMin)) - { - F32 u = LLSmoothInterpolation::getInterpolant(10.f); - mFloatingTargetMinBar = llmin(min, lerp(mFloatingTargetMinBar, min, u)); - mFloatingTargetMaxBar = llmax(max, lerp(mFloatingTargetMaxBar, max, u)); - F32 range_min = mAutoScaleMin ? mFloatingTargetMinBar : mTargetMinBar; - F32 range_max = mAutoScaleMax ? mFloatingTargetMaxBar : mTargetMaxBar; - F32 tick_value = 0.f; - calc_auto_scale_range(range_min, range_max, tick_value); - if (mAutoScaleMin) { mTargetMinBar = range_min; } - if (mAutoScaleMax) { mTargetMaxBar = range_max; } - if (mAutoScaleMin && mAutoScaleMax) - { - mTickSpacing = tick_value; - } - else - { - mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar); - } - } - - // start counting from actual min, not current, animating min, so that ticks don't float between numbers - // ensure ticks always hit 0 - S32 last_tick = S32_MIN; - S32 last_label = S32_MIN; - if (mTickSpacing > 0.f && value_scale > 0.f) - { - const S32 MIN_TICK_SPACING = mOrientation == HORIZONTAL ? 20 : 30; - const S32 MIN_LABEL_SPACING = mOrientation == HORIZONTAL ? 30 : 60; - const S32 TICK_LENGTH = 4; - const S32 TICK_WIDTH = 1; - - F32 start = mCurMinBar < 0.f - ? llceil(-mCurMinBar / mTickSpacing) * -mTickSpacing - : 0.f; - for (F32 tick_value = start; ;tick_value += mTickSpacing) - { - // clamp to S32_MAX / 2 to avoid floating point to integer overflow resulting in S32_MIN - const S32 tick_begin = llfloor(llmin((F32)(S32_MAX / 2), (tick_value - mCurMinBar)*value_scale)); - const S32 tick_end = tick_begin + TICK_WIDTH; - if (tick_begin < last_tick + MIN_TICK_SPACING) - { - continue; - } - last_tick = tick_begin; - - S32 decimal_digits = mDecimalDigits; - if (is_approx_equal((F32)(S32)tick_value, tick_value)) - { - decimal_digits = 0; - } - std::string tick_label = llformat("%.*f", decimal_digits, tick_value); - S32 tick_label_width = LLFontGL::getFontMonospace()->getWidth(tick_label); - if (mOrientation == HORIZONTAL) - { - if (tick_begin > last_label + MIN_LABEL_SPACING) - { - gl_rect_2d(bar_rect.mLeft, tick_end, bar_rect.mRight - TICK_LENGTH, tick_begin, LLColor4(1.f, 1.f, 1.f, 0.25f)); - LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, bar_rect.mRight, tick_begin, - LLColor4(1.f, 1.f, 1.f, 0.5f), - LLFontGL::LEFT, LLFontGL::VCENTER); - last_label = tick_begin; - } - else - { - gl_rect_2d(bar_rect.mLeft, tick_end, bar_rect.mRight - TICK_LENGTH/2, tick_begin, LLColor4(1.f, 1.f, 1.f, 0.1f)); - } - } - else - { - if (tick_begin > last_label + MIN_LABEL_SPACING) - { - gl_rect_2d(tick_begin, bar_rect.mTop, tick_end, bar_rect.mBottom - TICK_LENGTH, LLColor4(1.f, 1.f, 1.f, 0.25f)); - S32 label_pos = tick_begin - ll_round((F32)tick_label_width * ((F32)tick_begin / (F32)bar_rect.getWidth())); - LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, label_pos, bar_rect.mBottom - TICK_LENGTH, - LLColor4(1.f, 1.f, 1.f, 0.5f), - LLFontGL::LEFT, LLFontGL::TOP); - last_label = label_pos; - } - else - { - gl_rect_2d(tick_begin, bar_rect.mTop, tick_end, bar_rect.mBottom - TICK_LENGTH/2, LLColor4(1.f, 1.f, 1.f, 0.1f)); - } - } - // always draw one tick value past tick_end, so we can see part of the text, if possible - if (tick_value > mCurMaxBar) - { - break; - } - } - } -} +/** + * @file llstatbar.cpp + * @brief A little map of the world with network information + * + * $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 "linden_common.h" + +#include "llstatbar.h" + +#include "llmath.h" +#include "llui.h" +#include "llgl.h" +#include "llfontgl.h" + +#include "lluictrlfactory.h" +#include "lltracerecording.h" +#include "llcriticaldamp.h" +#include "lltooltip.h" +#include "lllocalcliprect.h" +#include +#include "lltrans.h" + +// rate at which to update display of value that is rapidly changing +const F32 MEAN_VALUE_UPDATE_TIME = 1.f / 4.f; +// time between value changes that qualifies as a "rapid change" +const F32Seconds RAPID_CHANGE_THRESHOLD(0.2f); +// maximum number of rapid changes in RAPID_CHANGE_WINDOW before switching over to displaying the mean +// instead of latest value +const S32 MAX_RAPID_CHANGES_PER_SEC = 10; +// period of time over which to measure rapid changes +const F32Seconds RAPID_CHANGE_WINDOW(1.f); + +F32 calc_tick_value(F32 min, F32 max) +{ + F32 range = max - min; + const S32 DIVISORS[] = {6, 8, 10, 4, 5}; + // try storing + S32 best_decimal_digit_count = S32_MAX; + S32 best_divisor = 10; + for (U32 divisor_idx = 0; divisor_idx < LL_ARRAY_SIZE(DIVISORS); divisor_idx++) + { + S32 divisor = DIVISORS[divisor_idx]; + F32 possible_tick_value = range / divisor; + S32 num_whole_digits = llceil(logf(llabs(min + possible_tick_value)) * OO_LN10); + for (S32 digit_count = -(num_whole_digits - 1); digit_count < 6; digit_count++) + { + F32 test_tick_value = min + (possible_tick_value * pow(10.0, digit_count)); + + if (is_approx_equal((F32)(S32)test_tick_value, test_tick_value)) + { + if (digit_count < best_decimal_digit_count) + { + best_decimal_digit_count = digit_count; + best_divisor = divisor; + } + break; + } + } + } + + return is_approx_equal(range, 0.f) ? 0.f : range / best_divisor; +} + +void calc_auto_scale_range(F32& min, F32& max, F32& tick) +{ + min = llmin(0.f, min, max); + max = llmax(0.f, min, max); + + const F32 RANGES[] = {0.f, 1.f, 1.5f, 2.f, 3.f, 5.f, 10.f}; + const F32 TICKS[] = {0.f, 0.25f, 0.5f, 1.f, 1.f, 1.f, 2.f }; + + const S32 num_digits_max = is_approx_equal(llabs(max), 0.f) + ? S32_MIN + 1 + : llceil(logf(llabs(max)) * OO_LN10); + const S32 num_digits_min = is_approx_equal(llabs(min), 0.f) + ? S32_MIN + 1 + : llceil(logf(llabs(min)) * OO_LN10); + + const S32 num_digits = llmax(num_digits_max, num_digits_min); + const F32 power_of_10 = pow(10.0, num_digits - 1); + const F32 starting_max = power_of_10 * ((max < 0.f) ? -1 : 1); + const F32 starting_min = power_of_10 * ((min < 0.f) ? -1 : 1); + + F32 cur_max = starting_max; + F32 cur_min = starting_min; + F32 out_max = max; + F32 out_min = min; + + F32 cur_tick_min = 0.f; + F32 cur_tick_max = 0.f; + + for (S32 range_idx = 0; range_idx < LL_ARRAY_SIZE(RANGES); range_idx++) + { + cur_max = starting_max * RANGES[range_idx]; + cur_min = starting_min * RANGES[range_idx]; + + if (min > 0.f && cur_min <= min) + { + out_min = cur_min; + cur_tick_min = TICKS[range_idx]; + } + if (max < 0.f && cur_max >= max) + { + out_max = cur_max; + cur_tick_max = TICKS[range_idx]; + } + } + + cur_max = starting_max; + cur_min = starting_min; + for (S32 range_idx = LL_ARRAY_SIZE(RANGES) - 1; range_idx >= 0; range_idx--) + { + cur_max = starting_max * RANGES[range_idx]; + cur_min = starting_min * RANGES[range_idx]; + + if (min < 0.f && cur_min <= min) + { + out_min = cur_min; + cur_tick_min = TICKS[range_idx]; + } + if (max > 0.f && cur_max >= max) + { + out_max = cur_max; + cur_tick_max = TICKS[range_idx]; + } + } + + tick = power_of_10 * llmax(cur_tick_min, cur_tick_max); + min = out_min; + max = out_max; +} + +LLStatBar::Params::Params() +: label("label"), + unit_label("unit_label"), + bar_min("bar_min", 0.f), + bar_max("bar_max", 0.f), + tick_spacing("tick_spacing", 0.f), + decimal_digits("decimal_digits", 3), + show_bar("show_bar", false), + show_median("show_median", false), + show_history("show_history", false), + scale_range("scale_range", true), + num_frames("num_frames", 200), + num_frames_short("num_frames_short", 20), + max_height("max_height", 100), + stat("stat"), + orientation("orientation", VERTICAL) +{ + changeDefault(follows.flags, FOLLOWS_TOP | FOLLOWS_LEFT); +} + +/////////////////////////////////////////////////////////////////////////////////// + +LLStatBar::LLStatBar(const Params& p) +: LLView(p), + mLabel(p.label), + mUnitLabel(p.unit_label), + mTargetMinBar(llmin(p.bar_min, p.bar_max)), + mTargetMaxBar(llmax(p.bar_max, p.bar_min)), + mCurMaxBar(p.bar_max), + mCurMinBar(0), + mDecimalDigits(p.decimal_digits), + mNumHistoryFrames(p.num_frames), + mNumShortHistoryFrames(p.num_frames_short), + mMaxHeight(p.max_height), + mDisplayBar(p.show_bar), + mShowMedian(p.show_median), + mDisplayHistory(p.show_history), + mOrientation(p.orientation), + mAutoScaleMax(!p.bar_max.isProvided()), + mAutoScaleMin(!p.bar_min.isProvided()), + mTickSpacing(p.tick_spacing), + mLastDisplayValue(0.f), + mStatType(STAT_NONE) +{ + mFloatingTargetMinBar = mTargetMinBar; + mFloatingTargetMaxBar = mTargetMaxBar; + + mStat.valid = NULL; + // tick value will be automatically calculated later + if (!p.tick_spacing.isProvided() && p.bar_min.isProvided() && p.bar_max.isProvided()) + { + mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar); + } + + setStat(p.stat); +} + +bool LLStatBar::handleHover(S32 x, S32 y, MASK mask) +{ + switch(mStatType) + { + case STAT_COUNT: + LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.countStatp->getDescription()).sticky_rect(calcScreenRect())); + break; + case STAT_EVENT: + LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.eventStatp->getDescription()).sticky_rect(calcScreenRect())); + break; + case STAT_SAMPLE: + LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.sampleStatp->getDescription()).sticky_rect(calcScreenRect())); + break; + default: + break; + } + return true; +} + +bool LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = LLView::handleMouseDown(x, y, mask); + if (!handled) + { + if (mDisplayBar) + { + if (mDisplayHistory || mOrientation == HORIZONTAL) + { + mDisplayBar = false; + mDisplayHistory = false; + } + else + { + mDisplayHistory = true; + } + } + else + { + mDisplayBar = true; + if (mOrientation == HORIZONTAL) + { + mDisplayHistory = true; + } + } + LLView* parent = getParent(); + parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), false); + } + return true; +} + +template +S32 calc_num_rapid_changes(LLTrace::PeriodicRecording& periodic_recording, const T& stat, const F32Seconds time_period) +{ + F32Seconds elapsed_time, + time_since_value_changed; + S32 num_rapid_changes = 0; + const F32Seconds RAPID_CHANGE_THRESHOLD = F32Seconds(0.3f); + F64 last_value = periodic_recording.getPrevRecording(1).getLastValue(stat); + + for (S32 i = 2; i < periodic_recording.getNumRecordedPeriods(); i++) + { + LLTrace::Recording& recording = periodic_recording.getPrevRecording(i); + F64 cur_value = recording.getLastValue(stat); + + if (last_value != cur_value) + { + if (time_since_value_changed < RAPID_CHANGE_THRESHOLD) num_rapid_changes++; + time_since_value_changed = (F32Seconds)0; + } + last_value = cur_value; + + elapsed_time += recording.getDuration(); + if (elapsed_time > time_period) break; + } + + return num_rapid_changes; +} + +void LLStatBar::draw() +{ + LLLocalClipRect _(getLocalRect()); + + LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording(); + LLTrace::Recording& last_frame_recording = frame_recording.getLastRecording(); + + std::string unit_label; + F32 current = 0, + min = 0, + max = 0, + mean = 0, + display_value = 0; + S32 num_frames = mDisplayHistory + ? mNumHistoryFrames + : mNumShortHistoryFrames; + S32 num_rapid_changes = 0; + S32 decimal_digits = mDecimalDigits; + + switch(mStatType) + { + case STAT_COUNT: + { + const LLTrace::StatType& count_stat = *mStat.countStatp; + + unit_label = std::string(count_stat.getUnitLabel()) + "/s"; + current = last_frame_recording.getPerSec(count_stat); + min = frame_recording.getPeriodMinPerSec(count_stat, num_frames); + max = frame_recording.getPeriodMaxPerSec(count_stat, num_frames); + mean = frame_recording.getPeriodMeanPerSec(count_stat, num_frames); + if (mShowMedian) + { + display_value = frame_recording.getPeriodMedianPerSec(count_stat, num_frames); + } + else + { + display_value = mean; + } + } + break; + case STAT_EVENT: + { + const LLTrace::StatType& event_stat = *mStat.eventStatp; + + unit_label = mUnitLabel.empty() ? event_stat.getUnitLabel() : mUnitLabel; + current = last_frame_recording.getLastValue(event_stat); + min = frame_recording.getPeriodMin(event_stat, num_frames); + max = frame_recording.getPeriodMax(event_stat, num_frames); + mean = frame_recording.getPeriodMean(event_stat, num_frames); + display_value = mean; + } + break; + case STAT_SAMPLE: + { + const LLTrace::StatType& sample_stat = *mStat.sampleStatp; + + unit_label = mUnitLabel.empty() ? sample_stat.getUnitLabel() : mUnitLabel; + current = last_frame_recording.getLastValue(sample_stat); + min = frame_recording.getPeriodMin(sample_stat, num_frames); + max = frame_recording.getPeriodMax(sample_stat, num_frames); + mean = frame_recording.getPeriodMean(sample_stat, num_frames); + num_rapid_changes = calc_num_rapid_changes(frame_recording, sample_stat, RAPID_CHANGE_WINDOW); + + if (mShowMedian) + { + display_value = frame_recording.getPeriodMedian(sample_stat, num_frames); + } + else if (num_rapid_changes / RAPID_CHANGE_WINDOW.value() > MAX_RAPID_CHANGES_PER_SEC) + { + display_value = mean; + } + else + { + display_value = current; + // always display current value, don't rate limit + mLastDisplayValue = current; + if (is_approx_equal((F32)(S32)display_value, display_value)) + { + decimal_digits = 0; + } + } + } + break; + default: + break; + } + + LLRect bar_rect; + if (mOrientation == HORIZONTAL) + { + bar_rect.mTop = llmax(5, getRect().getHeight() - 15); + bar_rect.mLeft = 0; + bar_rect.mRight = getRect().getWidth() - 40; + bar_rect.mBottom = llmin(bar_rect.mTop - 5, 0); + } + else // VERTICAL + { + bar_rect.mTop = llmax(5, getRect().getHeight() - 15); + bar_rect.mLeft = 0; + bar_rect.mRight = getRect().getWidth(); + bar_rect.mBottom = llmin(bar_rect.mTop - 5, 20); + } + + mCurMaxBar = LLSmoothInterpolation::lerp(mCurMaxBar, mTargetMaxBar, 0.05f); + mCurMinBar = LLSmoothInterpolation::lerp(mCurMinBar, mTargetMinBar, 0.05f); + + // rate limited updates + if (mLastDisplayValueTimer.getElapsedTimeF32() < MEAN_VALUE_UPDATE_TIME) + { + display_value = mLastDisplayValue; + } + else + { + mLastDisplayValueTimer.reset(); + } + drawLabelAndValue(display_value, unit_label, bar_rect, decimal_digits); + mLastDisplayValue = display_value; + + if (mDisplayBar && mStat.valid) + { + // Draw the tick marks. + LLGLSUIDefault gls_ui; + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + F32 value_scale; + if (mCurMaxBar == mCurMinBar) + { + value_scale = 0.f; + } + else + { + value_scale = (mOrientation == HORIZONTAL) + ? (bar_rect.getHeight())/(mCurMaxBar - mCurMinBar) + : (bar_rect.getWidth())/(mCurMaxBar - mCurMinBar); + } + + drawTicks(min, max, value_scale, bar_rect); + + // draw background bar. + gl_rect_2d(bar_rect.mLeft, bar_rect.mTop, bar_rect.mRight, bar_rect.mBottom, LLColor4(0.f, 0.f, 0.f, 0.25f)); + + // draw values + if (!llisnan(display_value) && frame_recording.getNumRecordedPeriods() != 0) + { + // draw min and max + S32 begin = (S32) ((min - mCurMinBar) * value_scale); + + if (begin < 0) + { + begin = 0; + } + + S32 end = (S32) ((max - mCurMinBar) * value_scale); + if (mOrientation == HORIZONTAL) + { + gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 0.25f)); + } + else // VERTICAL + { + gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.f, 0.f, 0.25f)); + } + + F32 span = (mOrientation == HORIZONTAL) + ? (bar_rect.getWidth()) + : (bar_rect.getHeight()); + + if (mDisplayHistory && mStat.valid) + { + const S32 num_values = frame_recording.getNumRecordedPeriods() - 1; + F32 min_value = 0.f, + max_value = 0.f; + + gGL.color4f(1.f, 0.f, 0.f, 1.f); + gGL.begin( LLRender::QUADS ); + const S32 max_frame = llmin(num_frames, num_values); + U32 num_samples = 0; + for (S32 i = 1; i <= max_frame; i++) + { + F32 offset = ((F32)i / (F32)num_frames) * span; + LLTrace::Recording& recording = frame_recording.getPrevRecording(i); + + switch(mStatType) + { + case STAT_COUNT: + min_value = recording.getPerSec(*mStat.countStatp); + max_value = min_value; + num_samples = recording.getSampleCount(*mStat.countStatp); + break; + case STAT_EVENT: + min_value = recording.getMin(*mStat.eventStatp); + max_value = recording.getMax(*mStat.eventStatp); + num_samples = recording.getSampleCount(*mStat.eventStatp); + break; + case STAT_SAMPLE: + min_value = recording.getMin(*mStat.sampleStatp); + max_value = recording.getMax(*mStat.sampleStatp); + num_samples = recording.getSampleCount(*mStat.sampleStatp); + break; + default: + break; + } + + if (!num_samples) continue; + + F32 min = (min_value - mCurMinBar) * value_scale; + F32 max = llmax(min + 1, (max_value - mCurMinBar) * value_scale); + if (mOrientation == HORIZONTAL) + { + gGL.vertex2f((F32)bar_rect.mRight - offset, max); + gGL.vertex2f((F32)bar_rect.mRight - offset, min); + gGL.vertex2f((F32)bar_rect.mRight - offset - 1, min); + gGL.vertex2f((F32)bar_rect.mRight - offset - 1, max); + } + else + { + gGL.vertex2f(min, (F32)bar_rect.mBottom + offset + 1); + gGL.vertex2f(min, (F32)bar_rect.mBottom + offset); + gGL.vertex2f(max, (F32)bar_rect.mBottom + offset); + gGL.vertex2f(max, (F32)bar_rect.mBottom + offset + 1 ); + } + } + gGL.end(); + } + else + { + S32 begin = (S32) ((current - mCurMinBar) * value_scale) - 1; + S32 end = (S32) ((current - mCurMinBar) * value_scale) + 1; + // draw current + if (mOrientation == HORIZONTAL) + { + gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 1.f)); + } + else + { + gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.f, 0.f, 1.f)); + } + } + + // draw mean bar + { + const S32 begin = (S32) ((mean - mCurMinBar) * value_scale) - 1; + const S32 end = (S32) ((mean - mCurMinBar) * value_scale) + 1; + if (mOrientation == HORIZONTAL) + { + gl_rect_2d(bar_rect.mLeft - 2, begin, bar_rect.mRight + 2, end, LLColor4(0.f, 1.f, 0.f, 1.f)); + } + else + { + gl_rect_2d(begin, bar_rect.mTop + 2, end, bar_rect.mBottom - 2, LLColor4(0.f, 1.f, 0.f, 1.f)); + } + } + } + } + + LLView::draw(); +} + +void LLStatBar::setStat(const std::string& stat_name) +{ + using namespace LLTrace; + + if (auto count_stat = StatType::getInstance(stat_name)) + { + mStat.countStatp = count_stat.get(); + mStatType = STAT_COUNT; + } + else if (auto event_stat = StatType::getInstance(stat_name)) + { + mStat.eventStatp = event_stat.get(); + mStatType = STAT_EVENT; + } + else if (auto sample_stat = StatType::getInstance(stat_name)) + { + mStat.sampleStatp = sample_stat.get(); + mStatType = STAT_SAMPLE; + } +} + +void LLStatBar::setRange(F32 bar_min, F32 bar_max) +{ + mTargetMinBar = llmin(bar_min, bar_max); + mTargetMaxBar = llmax(bar_min, bar_max); + mFloatingTargetMinBar = mTargetMinBar; + mFloatingTargetMaxBar = mTargetMaxBar; + mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar); +} + +LLRect LLStatBar::getRequiredRect() +{ + LLRect rect; + + if (mDisplayBar) + { + if (mDisplayHistory) + { + rect.mTop = mMaxHeight; + } + else + { + rect.mTop = 40; + } + } + else + { + rect.mTop = 14; + } + return rect; +} + +void LLStatBar::drawLabelAndValue( F32 value, std::string &label, LLRect &bar_rect, S32 decimal_digits ) +{ + LLFontGL::getFontMonospace()->renderUTF8(mLabel, 0, 0, getRect().getHeight(), LLColor4(1.f, 1.f, 1.f, 1.f), + LLFontGL::LEFT, LLFontGL::TOP); + + std::string value_str = !llisnan(value) + ? llformat("%10.*f %s", decimal_digits, value, label.c_str()) + : LLTrans::getString("na"); + + // Draw the current value. + if (mOrientation == HORIZONTAL) + { + LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(), + LLColor4(1.f, 1.f, 1.f, 1.f), + LLFontGL::RIGHT, LLFontGL::TOP); + } + else + { + LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(), + LLColor4(1.f, 1.f, 1.f, 1.f), + LLFontGL::RIGHT, LLFontGL::TOP); + } +} + +void LLStatBar::drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect ) +{ + if (!llisnan(min) && (mAutoScaleMax || mAutoScaleMin)) + { + F32 u = LLSmoothInterpolation::getInterpolant(10.f); + mFloatingTargetMinBar = llmin(min, lerp(mFloatingTargetMinBar, min, u)); + mFloatingTargetMaxBar = llmax(max, lerp(mFloatingTargetMaxBar, max, u)); + F32 range_min = mAutoScaleMin ? mFloatingTargetMinBar : mTargetMinBar; + F32 range_max = mAutoScaleMax ? mFloatingTargetMaxBar : mTargetMaxBar; + F32 tick_value = 0.f; + calc_auto_scale_range(range_min, range_max, tick_value); + if (mAutoScaleMin) { mTargetMinBar = range_min; } + if (mAutoScaleMax) { mTargetMaxBar = range_max; } + if (mAutoScaleMin && mAutoScaleMax) + { + mTickSpacing = tick_value; + } + else + { + mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar); + } + } + + // start counting from actual min, not current, animating min, so that ticks don't float between numbers + // ensure ticks always hit 0 + S32 last_tick = S32_MIN; + S32 last_label = S32_MIN; + if (mTickSpacing > 0.f && value_scale > 0.f) + { + const S32 MIN_TICK_SPACING = mOrientation == HORIZONTAL ? 20 : 30; + const S32 MIN_LABEL_SPACING = mOrientation == HORIZONTAL ? 30 : 60; + const S32 TICK_LENGTH = 4; + const S32 TICK_WIDTH = 1; + + F32 start = mCurMinBar < 0.f + ? llceil(-mCurMinBar / mTickSpacing) * -mTickSpacing + : 0.f; + for (F32 tick_value = start; ;tick_value += mTickSpacing) + { + // clamp to S32_MAX / 2 to avoid floating point to integer overflow resulting in S32_MIN + const S32 tick_begin = llfloor(llmin((F32)(S32_MAX / 2), (tick_value - mCurMinBar)*value_scale)); + const S32 tick_end = tick_begin + TICK_WIDTH; + if (tick_begin < last_tick + MIN_TICK_SPACING) + { + continue; + } + last_tick = tick_begin; + + S32 decimal_digits = mDecimalDigits; + if (is_approx_equal((F32)(S32)tick_value, tick_value)) + { + decimal_digits = 0; + } + std::string tick_label = llformat("%.*f", decimal_digits, tick_value); + S32 tick_label_width = LLFontGL::getFontMonospace()->getWidth(tick_label); + if (mOrientation == HORIZONTAL) + { + if (tick_begin > last_label + MIN_LABEL_SPACING) + { + gl_rect_2d(bar_rect.mLeft, tick_end, bar_rect.mRight - TICK_LENGTH, tick_begin, LLColor4(1.f, 1.f, 1.f, 0.25f)); + LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, bar_rect.mRight, tick_begin, + LLColor4(1.f, 1.f, 1.f, 0.5f), + LLFontGL::LEFT, LLFontGL::VCENTER); + last_label = tick_begin; + } + else + { + gl_rect_2d(bar_rect.mLeft, tick_end, bar_rect.mRight - TICK_LENGTH/2, tick_begin, LLColor4(1.f, 1.f, 1.f, 0.1f)); + } + } + else + { + if (tick_begin > last_label + MIN_LABEL_SPACING) + { + gl_rect_2d(tick_begin, bar_rect.mTop, tick_end, bar_rect.mBottom - TICK_LENGTH, LLColor4(1.f, 1.f, 1.f, 0.25f)); + S32 label_pos = tick_begin - ll_round((F32)tick_label_width * ((F32)tick_begin / (F32)bar_rect.getWidth())); + LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, label_pos, bar_rect.mBottom - TICK_LENGTH, + LLColor4(1.f, 1.f, 1.f, 0.5f), + LLFontGL::LEFT, LLFontGL::TOP); + last_label = label_pos; + } + else + { + gl_rect_2d(tick_begin, bar_rect.mTop, tick_end, bar_rect.mBottom - TICK_LENGTH/2, LLColor4(1.f, 1.f, 1.f, 0.1f)); + } + } + // always draw one tick value past tick_end, so we can see part of the text, if possible + if (tick_value > mCurMaxBar) + { + break; + } + } + } +} diff --git a/indra/llui/llstatbar.h b/indra/llui/llstatbar.h index 84e9ac6ecb..c36a138566 100644 --- a/indra/llui/llstatbar.h +++ b/indra/llui/llstatbar.h @@ -1,119 +1,119 @@ -/** - * @file llstatbar.h - * @brief A little map of the world with network information - * - * $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$ - */ - -#ifndef LL_LLSTATBAR_H -#define LL_LLSTATBAR_H - -#include "llview.h" -#include "llframetimer.h" -#include "lltracerecording.h" - -class LLStatBar : public LLView -{ -public: - - struct Params : public LLInitParam::Block - { - Optional label, - unit_label; - - Optional bar_min, - bar_max, - tick_spacing; - - Optional show_bar, - show_history, - scale_range, - show_median; // default is mean - - Optional decimal_digits, - num_frames, - num_frames_short, - max_height; - Optional stat; - Optional orientation; - - Params(); - }; - LLStatBar(const Params&); - - virtual void draw(); - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleHover(S32 x, S32 y, MASK mask); - - void setStat(const std::string& stat_name); - - void setRange(F32 bar_min, F32 bar_max); - void getRange(F32& bar_min, F32& bar_max) { bar_min = mTargetMinBar; bar_max = mTargetMaxBar; } - - /*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options. - -private: - void drawLabelAndValue( F32 mean, std::string &unit_label, LLRect &bar_rect, S32 decimal_digits ); - void drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect ); - - F32 mTargetMinBar, - mTargetMaxBar, - mFloatingTargetMinBar, - mFloatingTargetMaxBar, - mCurMaxBar, - mCurMinBar, - mTickSpacing; - S32 mDecimalDigits, - mNumHistoryFrames, - mNumShortHistoryFrames; - S32 mMaxHeight; - EOrientation mOrientation; - F32 mLastDisplayValue; - LLFrameTimer mLastDisplayValueTimer; - - enum - { - STAT_NONE, - STAT_COUNT, - STAT_EVENT, - STAT_SAMPLE - } mStatType; - - union - { - void* valid; - const LLTrace::StatType* countStatp; - const LLTrace::StatType* eventStatp; - const LLTrace::StatType* sampleStatp; - } mStat; - - LLUIString mLabel; - std::string mUnitLabel; - - bool mDisplayBar, // Display the bar graph. - mDisplayHistory, - mShowMedian, - mAutoScaleMax, - mAutoScaleMin; -}; - -#endif +/** + * @file llstatbar.h + * @brief A little map of the world with network information + * + * $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$ + */ + +#ifndef LL_LLSTATBAR_H +#define LL_LLSTATBAR_H + +#include "llview.h" +#include "llframetimer.h" +#include "lltracerecording.h" + +class LLStatBar : public LLView +{ +public: + + struct Params : public LLInitParam::Block + { + Optional label, + unit_label; + + Optional bar_min, + bar_max, + tick_spacing; + + Optional show_bar, + show_history, + scale_range, + show_median; // default is mean + + Optional decimal_digits, + num_frames, + num_frames_short, + max_height; + Optional stat; + Optional orientation; + + Params(); + }; + LLStatBar(const Params&); + + virtual void draw(); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + + void setStat(const std::string& stat_name); + + void setRange(F32 bar_min, F32 bar_max); + void getRange(F32& bar_min, F32& bar_max) { bar_min = mTargetMinBar; bar_max = mTargetMaxBar; } + + /*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options. + +private: + void drawLabelAndValue( F32 mean, std::string &unit_label, LLRect &bar_rect, S32 decimal_digits ); + void drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect ); + + F32 mTargetMinBar, + mTargetMaxBar, + mFloatingTargetMinBar, + mFloatingTargetMaxBar, + mCurMaxBar, + mCurMinBar, + mTickSpacing; + S32 mDecimalDigits, + mNumHistoryFrames, + mNumShortHistoryFrames; + S32 mMaxHeight; + EOrientation mOrientation; + F32 mLastDisplayValue; + LLFrameTimer mLastDisplayValueTimer; + + enum + { + STAT_NONE, + STAT_COUNT, + STAT_EVENT, + STAT_SAMPLE + } mStatType; + + union + { + void* valid; + const LLTrace::StatType* countStatp; + const LLTrace::StatType* eventStatp; + const LLTrace::StatType* sampleStatp; + } mStat; + + LLUIString mLabel; + std::string mUnitLabel; + + bool mDisplayBar, // Display the bar graph. + mDisplayHistory, + mShowMedian, + mAutoScaleMax, + mAutoScaleMin; +}; + +#endif diff --git a/indra/llui/llstatgraph.cpp b/indra/llui/llstatgraph.cpp index 58532fb22d..d37f927073 100644 --- a/indra/llui/llstatgraph.cpp +++ b/indra/llui/llstatgraph.cpp @@ -1,126 +1,126 @@ -/** - * @file llstatgraph.cpp - * @brief Simpler compact stat graph with tooltip - * - * $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$ - */ - -//#include "llviewerprecompiledheaders.h" -#include "linden_common.h" - -#include "llstatgraph.h" -#include "llrender.h" - -#include "llmath.h" -#include "llui.h" -#include "llgl.h" -#include "llglheaders.h" -#include "lltracerecording.h" -#include "lltracethreadrecorder.h" -//#include "llviewercontrol.h" - -/////////////////////////////////////////////////////////////////////////////////// - -LLStatGraph::LLStatGraph(const Params& p) -: LLView(p), - mMin(p.min), - mMax(p.max), - mPerSec(p.per_sec), - mPrecision(p.precision), - mValue(p.value), - mUnits(p.units), - mNewStatFloatp(p.stat.count_stat_float) -{ - setToolTip(p.name()); - - for(LLInitParam::ParamIterator::const_iterator it = p.thresholds.threshold.begin(), end_it = p.thresholds.threshold.end(); - it != end_it; - ++it) - { - mThresholds.push_back(Threshold(it->value(), it->color)); - } -} - -void LLStatGraph::draw() -{ - F32 range, frac; - range = mMax - mMin; - if (mNewStatFloatp) - { - LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecording(); - - if (mPerSec) - { - mValue = recording.getPerSec(*mNewStatFloatp); - } - else - { - mValue = recording.getSum(*mNewStatFloatp); - } - } - - frac = (mValue - mMin) / range; - frac = llmax(0.f, frac); - frac = llmin(1.f, frac); - - if (mUpdateTimer.getElapsedTimeF32() > 0.5f) - { - std::string format_str; - std::string tmp_str; - format_str = llformat("%%s%%.%df%%s", mPrecision); - tmp_str = llformat(format_str.c_str(), mLabel.c_str(), mValue, mUnits.c_str()); - setToolTip(tmp_str); - - mUpdateTimer.reset(); - } - - LLColor4 color; - - threshold_vec_t::iterator it = std::lower_bound(mThresholds.begin(), mThresholds.end(), Threshold(mValue / mMax, LLUIColor())); - - if (it != mThresholds.begin()) - { - it--; - } - - color = LLUIColorTable::instance().getColor( "MenuDefaultBgColor" ); - gGL.color4fv(color.mV); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, true); - - gGL.color4fv(LLColor4::black.mV); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, false); - - color = it->mColor; - gGL.color4fv(color.mV); - gl_rect_2d(1, ll_round(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, true); -} - -void LLStatGraph::setMin(const F32 min) -{ - mMin = min; -} - -void LLStatGraph::setMax(const F32 max) -{ - mMax = max; -} - +/** + * @file llstatgraph.cpp + * @brief Simpler compact stat graph with tooltip + * + * $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$ + */ + +//#include "llviewerprecompiledheaders.h" +#include "linden_common.h" + +#include "llstatgraph.h" +#include "llrender.h" + +#include "llmath.h" +#include "llui.h" +#include "llgl.h" +#include "llglheaders.h" +#include "lltracerecording.h" +#include "lltracethreadrecorder.h" +//#include "llviewercontrol.h" + +/////////////////////////////////////////////////////////////////////////////////// + +LLStatGraph::LLStatGraph(const Params& p) +: LLView(p), + mMin(p.min), + mMax(p.max), + mPerSec(p.per_sec), + mPrecision(p.precision), + mValue(p.value), + mUnits(p.units), + mNewStatFloatp(p.stat.count_stat_float) +{ + setToolTip(p.name()); + + for(LLInitParam::ParamIterator::const_iterator it = p.thresholds.threshold.begin(), end_it = p.thresholds.threshold.end(); + it != end_it; + ++it) + { + mThresholds.push_back(Threshold(it->value(), it->color)); + } +} + +void LLStatGraph::draw() +{ + F32 range, frac; + range = mMax - mMin; + if (mNewStatFloatp) + { + LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecording(); + + if (mPerSec) + { + mValue = recording.getPerSec(*mNewStatFloatp); + } + else + { + mValue = recording.getSum(*mNewStatFloatp); + } + } + + frac = (mValue - mMin) / range; + frac = llmax(0.f, frac); + frac = llmin(1.f, frac); + + if (mUpdateTimer.getElapsedTimeF32() > 0.5f) + { + std::string format_str; + std::string tmp_str; + format_str = llformat("%%s%%.%df%%s", mPrecision); + tmp_str = llformat(format_str.c_str(), mLabel.c_str(), mValue, mUnits.c_str()); + setToolTip(tmp_str); + + mUpdateTimer.reset(); + } + + LLColor4 color; + + threshold_vec_t::iterator it = std::lower_bound(mThresholds.begin(), mThresholds.end(), Threshold(mValue / mMax, LLUIColor())); + + if (it != mThresholds.begin()) + { + it--; + } + + color = LLUIColorTable::instance().getColor( "MenuDefaultBgColor" ); + gGL.color4fv(color.mV); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, true); + + gGL.color4fv(LLColor4::black.mV); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, false); + + color = it->mColor; + gGL.color4fv(color.mV); + gl_rect_2d(1, ll_round(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, true); +} + +void LLStatGraph::setMin(const F32 min) +{ + mMin = min; +} + +void LLStatGraph::setMax(const F32 max) +{ + mMax = max; +} + diff --git a/indra/llui/llstatgraph.h b/indra/llui/llstatgraph.h index 1c1b707688..c254821870 100644 --- a/indra/llui/llstatgraph.h +++ b/indra/llui/llstatgraph.h @@ -1,141 +1,141 @@ -/** - * @file llstatgraph.h - * @brief Simpler compact stat graph with tooltip - * - * $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_LLSTATGRAPH_H -#define LL_LLSTATGRAPH_H - -#include "llview.h" -#include "llframetimer.h" -#include "v4color.h" -#include "lltrace.h" - -class LLStatGraph : public LLView -{ -public: - struct ThresholdParams : public LLInitParam::Block - { - Mandatory value; - Optional color; - - ThresholdParams() - : value("value"), - color("color", LLColor4::white) - {} - }; - - struct Thresholds : public LLInitParam::Block - { - Multiple threshold; - - Thresholds() - : threshold("threshold") - {} - }; - - struct StatParams : public LLInitParam::ChoiceBlock - { - Alternative* > count_stat_float; - Alternative* > event_stat_float; - Alternative* > sample_stat_float; - }; - - struct Params : public LLInitParam::Block - { - Mandatory stat; - Optional label, - units; - Optional precision; - Optional min, - max; - Optional per_sec; - Optional value; - - Optional thresholds; - - Params() - : stat("stat"), - label("label"), - units("units"), - precision("precision", 0), - min("min", 0.f), - max("max", 125.f), - per_sec("per_sec", true), - value("value", 0.f), - thresholds("thresholds") - { - Thresholds _thresholds; - _thresholds.threshold.add(ThresholdParams().value(0.f).color(LLColor4::green)) - .add(ThresholdParams().value(0.33f).color(LLColor4::yellow)) - .add(ThresholdParams().value(0.5f).color(LLColor4::red)) - .add(ThresholdParams().value(0.75f).color(LLColor4::red)); - thresholds = _thresholds; - } - }; - LLStatGraph(const Params&); - - void setMin(const F32 min); - void setMax(const F32 max); - - virtual void draw(); - - /*virtual*/ void setValue(const LLSD& value); - -private: - LLTrace::StatType* mNewStatFloatp; - - bool mPerSec; - - F32 mValue; - - F32 mMin; - F32 mMax; - LLFrameTimer mUpdateTimer; - std::string mLabel; - std::string mUnits; - S32 mPrecision; // Num of digits of precision after dot - - struct Threshold - { - Threshold(F32 value, const LLUIColor& color) - : mValue(value), - mColor(color) - {} - - F32 mValue; - LLUIColor mColor; - bool operator <(const Threshold& other) const - { - return mValue < other.mValue; - } - }; - typedef std::vector threshold_vec_t; - threshold_vec_t mThresholds; - //S32 mNumThresholds; - //F32 mThresholds[4]; - //LLColor4 mThresholdColors[4]; -}; - -#endif // LL_LLSTATGRAPH_H +/** + * @file llstatgraph.h + * @brief Simpler compact stat graph with tooltip + * + * $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_LLSTATGRAPH_H +#define LL_LLSTATGRAPH_H + +#include "llview.h" +#include "llframetimer.h" +#include "v4color.h" +#include "lltrace.h" + +class LLStatGraph : public LLView +{ +public: + struct ThresholdParams : public LLInitParam::Block + { + Mandatory value; + Optional color; + + ThresholdParams() + : value("value"), + color("color", LLColor4::white) + {} + }; + + struct Thresholds : public LLInitParam::Block + { + Multiple threshold; + + Thresholds() + : threshold("threshold") + {} + }; + + struct StatParams : public LLInitParam::ChoiceBlock + { + Alternative* > count_stat_float; + Alternative* > event_stat_float; + Alternative* > sample_stat_float; + }; + + struct Params : public LLInitParam::Block + { + Mandatory stat; + Optional label, + units; + Optional precision; + Optional min, + max; + Optional per_sec; + Optional value; + + Optional thresholds; + + Params() + : stat("stat"), + label("label"), + units("units"), + precision("precision", 0), + min("min", 0.f), + max("max", 125.f), + per_sec("per_sec", true), + value("value", 0.f), + thresholds("thresholds") + { + Thresholds _thresholds; + _thresholds.threshold.add(ThresholdParams().value(0.f).color(LLColor4::green)) + .add(ThresholdParams().value(0.33f).color(LLColor4::yellow)) + .add(ThresholdParams().value(0.5f).color(LLColor4::red)) + .add(ThresholdParams().value(0.75f).color(LLColor4::red)); + thresholds = _thresholds; + } + }; + LLStatGraph(const Params&); + + void setMin(const F32 min); + void setMax(const F32 max); + + virtual void draw(); + + /*virtual*/ void setValue(const LLSD& value); + +private: + LLTrace::StatType* mNewStatFloatp; + + bool mPerSec; + + F32 mValue; + + F32 mMin; + F32 mMax; + LLFrameTimer mUpdateTimer; + std::string mLabel; + std::string mUnits; + S32 mPrecision; // Num of digits of precision after dot + + struct Threshold + { + Threshold(F32 value, const LLUIColor& color) + : mValue(value), + mColor(color) + {} + + F32 mValue; + LLUIColor mColor; + bool operator <(const Threshold& other) const + { + return mValue < other.mValue; + } + }; + typedef std::vector threshold_vec_t; + threshold_vec_t mThresholds; + //S32 mNumThresholds; + //F32 mThresholds[4]; + //LLColor4 mThresholdColors[4]; +}; + +#endif // LL_LLSTATGRAPH_H diff --git a/indra/llui/llstatview.cpp b/indra/llui/llstatview.cpp index c2da7b5053..1c7656ecd2 100644 --- a/indra/llui/llstatview.cpp +++ b/indra/llui/llstatview.cpp @@ -1,64 +1,64 @@ -/** - * @file llstatview.cpp - * @brief Container for all statistics info. - * - * $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 "linden_common.h" - -#include "llstatview.h" - -#include "llerror.h" -#include "llstatbar.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llui.h" - -#include "llstatbar.h" - -LLStatView::LLStatView(const LLStatView::Params& p) -: LLContainerView(p), - mSetting(p.setting) -{ - bool isopen = getDisplayChildren(); - if (mSetting.length() > 0) - { - isopen = LLUI::getInstance()->mSettingGroups["config"]->getBOOL(mSetting); - } - setDisplayChildren(isopen); -} - -LLStatView::~LLStatView() -{ - // Children all cleaned up by default view destructor. - if (mSetting.length() > 0) - { - bool isopen = getDisplayChildren(); - LLUI::getInstance()->mSettingGroups["config"]->setBOOL(mSetting, isopen); - } -} - -static StatViewRegistry::Register r1("stat_bar"); -static StatViewRegistry::Register r2("stat_view"); -// stat_view can be a child of panels/etc. -static LLDefaultChildRegistry::Register r3("stat_view"); +/** + * @file llstatview.cpp + * @brief Container for all statistics info. + * + * $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 "linden_common.h" + +#include "llstatview.h" + +#include "llerror.h" +#include "llstatbar.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llui.h" + +#include "llstatbar.h" + +LLStatView::LLStatView(const LLStatView::Params& p) +: LLContainerView(p), + mSetting(p.setting) +{ + bool isopen = getDisplayChildren(); + if (mSetting.length() > 0) + { + isopen = LLUI::getInstance()->mSettingGroups["config"]->getBOOL(mSetting); + } + setDisplayChildren(isopen); +} + +LLStatView::~LLStatView() +{ + // Children all cleaned up by default view destructor. + if (mSetting.length() > 0) + { + bool isopen = getDisplayChildren(); + LLUI::getInstance()->mSettingGroups["config"]->setBOOL(mSetting, isopen); + } +} + +static StatViewRegistry::Register r1("stat_bar"); +static StatViewRegistry::Register r2("stat_view"); +// stat_view can be a child of panels/etc. +static LLDefaultChildRegistry::Register r3("stat_view"); diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index 19d98824c1..abf6e1284b 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -1,104 +1,104 @@ -/** - * @file llstyle.cpp - * @brief Text style class - * - * $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 "linden_common.h" - -#include "llstyle.h" - -#include "llfontgl.h" -#include "llstring.h" -#include "llui.h" - -LLStyle::Params::Params() -: visible("visible", true), - drop_shadow("drop_shadow", LLFontGL::NO_SHADOW), - color("color", LLColor4::black), - readonly_color("readonly_color", LLColor4::black), - selected_color("selected_color", LLColor4::black), - font("font", LLFontGL::getFontMonospace()), - image("image"), - link_href("href"), - is_link("is_link") -{} - - -LLStyle::LLStyle(const LLStyle::Params& p) -: mVisible(p.visible), - mColor(p.color), - mReadOnlyColor(p.readonly_color), - mSelectedColor(p.selected_color), - mFont(p.font()), - mLink(p.link_href), - mIsLink(p.is_link.isProvided() ? p.is_link : !p.link_href().empty()), - mDropShadow(p.drop_shadow), - mImagep(p.image()) -{} - -void LLStyle::setFont(const LLFontGL* font) -{ - mFont = font; -} - - -const LLFontGL* LLStyle::getFont() const -{ - return mFont; -} - -void LLStyle::setLinkHREF(const std::string& href) -{ - mLink = href; -} - -bool LLStyle::isLink() const -{ - return mIsLink; -} - -bool LLStyle::isVisible() const -{ - return mVisible; -} - -void LLStyle::setVisible(bool is_visible) -{ - mVisible = is_visible; -} - -LLPointer LLStyle::getImage() const -{ - return mImagep; -} - -void LLStyle::setImage(const LLUUID& src) -{ - mImagep = LLUI::getUIImageByID(src); -} - -void LLStyle::setImage(const std::string& name) -{ - mImagep = LLUI::getUIImage(name); -} +/** + * @file llstyle.cpp + * @brief Text style class + * + * $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 "linden_common.h" + +#include "llstyle.h" + +#include "llfontgl.h" +#include "llstring.h" +#include "llui.h" + +LLStyle::Params::Params() +: visible("visible", true), + drop_shadow("drop_shadow", LLFontGL::NO_SHADOW), + color("color", LLColor4::black), + readonly_color("readonly_color", LLColor4::black), + selected_color("selected_color", LLColor4::black), + font("font", LLFontGL::getFontMonospace()), + image("image"), + link_href("href"), + is_link("is_link") +{} + + +LLStyle::LLStyle(const LLStyle::Params& p) +: mVisible(p.visible), + mColor(p.color), + mReadOnlyColor(p.readonly_color), + mSelectedColor(p.selected_color), + mFont(p.font()), + mLink(p.link_href), + mIsLink(p.is_link.isProvided() ? p.is_link : !p.link_href().empty()), + mDropShadow(p.drop_shadow), + mImagep(p.image()) +{} + +void LLStyle::setFont(const LLFontGL* font) +{ + mFont = font; +} + + +const LLFontGL* LLStyle::getFont() const +{ + return mFont; +} + +void LLStyle::setLinkHREF(const std::string& href) +{ + mLink = href; +} + +bool LLStyle::isLink() const +{ + return mIsLink; +} + +bool LLStyle::isVisible() const +{ + return mVisible; +} + +void LLStyle::setVisible(bool is_visible) +{ + mVisible = is_visible; +} + +LLPointer LLStyle::getImage() const +{ + return mImagep; +} + +void LLStyle::setImage(const LLUUID& src) +{ + mImagep = LLUI::getUIImageByID(src); +} + +void LLStyle::setImage(const std::string& name) +{ + mImagep = LLUI::getUIImage(name); +} diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index 57b839e38d..7dbccfff87 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -1,118 +1,118 @@ -/** - * @file llstyle.h - * @brief Text style class - * - * $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$ - */ - -#ifndef LL_LLSTYLE_H -#define LL_LLSTYLE_H - -#include "v4color.h" -#include "llui.h" -#include "llinitparam.h" -#include "lluiimage.h" - -class LLFontGL; - -class LLStyle : public LLRefCount -{ -public: - struct Params : public LLInitParam::Block - { - Optional visible; - Optional drop_shadow; - Optional color, - readonly_color, - selected_color; - Optional font; - Optional image; - Optional link_href; - Optional is_link; - Params(); - }; - LLStyle(const Params& p = Params()); -public: - const LLUIColor& getColor() const { return mColor; } - void setColor(const LLUIColor &color) { mColor = color; } - - const LLUIColor& getReadOnlyColor() const { return mReadOnlyColor; } - void setReadOnlyColor(const LLUIColor& color) { mReadOnlyColor = color; } - - const LLUIColor& getSelectedColor() const { return mSelectedColor; } - void setSelectedColor(const LLUIColor& color) { mSelectedColor = color; } - - bool isVisible() const; - void setVisible(bool is_visible); - - LLFontGL::ShadowType getShadowType() const { return mDropShadow; } - - void setFont(const LLFontGL* font); - const LLFontGL* getFont() const; - - const std::string& getLinkHREF() const { return mLink; } - void setLinkHREF(const std::string& href); - bool isLink() const; - - LLPointer getImage() const; - void setImage(const LLUUID& src); - void setImage(const std::string& name); - - bool isImage() const { return mImagep.notNull(); } - - bool operator==(const LLStyle &rhs) const - { - return - mVisible == rhs.mVisible - && mColor == rhs.mColor - && mReadOnlyColor == rhs.mReadOnlyColor - && mSelectedColor == rhs.mSelectedColor - && mFont == rhs.mFont - && mLink == rhs.mLink - && mImagep == rhs.mImagep - && mDropShadow == rhs.mDropShadow; - } - - bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); } - -public: - LLFontGL::ShadowType mDropShadow; - -protected: - ~LLStyle() { } - -private: - bool mVisible; - LLUIColor mColor; - LLUIColor mReadOnlyColor; - LLUIColor mSelectedColor; - std::string mFontName; - const LLFontGL* mFont; - std::string mLink; - bool mIsLink; - LLPointer mImagep; -}; - -typedef LLPointer LLStyleSP; -typedef LLPointer LLStyleConstSP; - -#endif // LL_LLSTYLE_H +/** + * @file llstyle.h + * @brief Text style class + * + * $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$ + */ + +#ifndef LL_LLSTYLE_H +#define LL_LLSTYLE_H + +#include "v4color.h" +#include "llui.h" +#include "llinitparam.h" +#include "lluiimage.h" + +class LLFontGL; + +class LLStyle : public LLRefCount +{ +public: + struct Params : public LLInitParam::Block + { + Optional visible; + Optional drop_shadow; + Optional color, + readonly_color, + selected_color; + Optional font; + Optional image; + Optional link_href; + Optional is_link; + Params(); + }; + LLStyle(const Params& p = Params()); +public: + const LLUIColor& getColor() const { return mColor; } + void setColor(const LLUIColor &color) { mColor = color; } + + const LLUIColor& getReadOnlyColor() const { return mReadOnlyColor; } + void setReadOnlyColor(const LLUIColor& color) { mReadOnlyColor = color; } + + const LLUIColor& getSelectedColor() const { return mSelectedColor; } + void setSelectedColor(const LLUIColor& color) { mSelectedColor = color; } + + bool isVisible() const; + void setVisible(bool is_visible); + + LLFontGL::ShadowType getShadowType() const { return mDropShadow; } + + void setFont(const LLFontGL* font); + const LLFontGL* getFont() const; + + const std::string& getLinkHREF() const { return mLink; } + void setLinkHREF(const std::string& href); + bool isLink() const; + + LLPointer getImage() const; + void setImage(const LLUUID& src); + void setImage(const std::string& name); + + bool isImage() const { return mImagep.notNull(); } + + bool operator==(const LLStyle &rhs) const + { + return + mVisible == rhs.mVisible + && mColor == rhs.mColor + && mReadOnlyColor == rhs.mReadOnlyColor + && mSelectedColor == rhs.mSelectedColor + && mFont == rhs.mFont + && mLink == rhs.mLink + && mImagep == rhs.mImagep + && mDropShadow == rhs.mDropShadow; + } + + bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); } + +public: + LLFontGL::ShadowType mDropShadow; + +protected: + ~LLStyle() { } + +private: + bool mVisible; + LLUIColor mColor; + LLUIColor mReadOnlyColor; + LLUIColor mSelectedColor; + std::string mFontName; + const LLFontGL* mFont; + std::string mLink; + bool mIsLink; + LLPointer mImagep; +}; + +typedef LLPointer LLStyleSP; +typedef LLPointer LLStyleConstSP; + +#endif // LL_LLSTYLE_H diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index a55e244711..752ef47d14 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -1,2204 +1,2204 @@ -/** - * @file lltabcontainer.cpp - * @brief LLTabContainer class - * - * $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 "linden_common.h" - -#include "lltabcontainer.h" -#include "llviewereventrecorder.h" -#include "llfocusmgr.h" -#include "lllocalcliprect.h" -#include "llrect.h" -#include "llresizehandle.h" -#include "lltextbox.h" -#include "llcriticaldamp.h" -#include "lluictrlfactory.h" -#include "llrender.h" -#include "llfloater.h" -#include "lltrans.h" -#include "lluiusage.h" - -//---------------------------------------------------------------------------- - -// Implementation Notes: -// - Each tab points to a LLPanel (see LLTabTuple below) -// - When a tab is selected, the validation callback -// (LLUICtrl::mValidateSignal) is called -// - If the validation callback returns true (or none is provided), -// the tab is changed and the commit callback -// (LLUICtrl::mCommitSignal) is called -// - Callbacks pass the LLTabContainer as the control, -// and the NAME of the selected PANEL as the LLSD data - -//---------------------------------------------------------------------------- - -const F32 SCROLL_STEP_TIME = 0.4f; -const F32 SCROLL_DELAY_TIME = 0.5f; - -void LLTabContainer::TabPositions::declareValues() -{ - declare("top", LLTabContainer::TOP); - declare("bottom", LLTabContainer::BOTTOM); - declare("left", LLTabContainer::LEFT); -} - -//---------------------------------------------------------------------------- - -// Structure used to map tab buttons to and from tab panels -class LLTabTuple -{ -public: - LLTabTuple( LLTabContainer* c, LLPanel* p, LLButton* b, LLTextBox* placeholder = NULL) - : - mTabContainer(c), - mTabPanel(p), - mButton(b), - mOldState(false), - mPlaceholderText(placeholder), - mPadding(0), - mVisible(true) - {} - - LLTabContainer* mTabContainer; - LLPanel* mTabPanel; - LLButton* mButton; - bool mOldState; - LLTextBox* mPlaceholderText; - S32 mPadding; - - mutable bool mVisible; -}; - -//---------------------------------------------------------------------------- - -//============================================================================ -/* - * @file lltabcontainer.cpp - * @brief class implements LLButton with LLIconCtrl on it - */ -class LLCustomButtonIconCtrl : public LLButton -{ -public: - struct Params - : public LLInitParam::Block - { - // LEFT, RIGHT, TOP, BOTTOM paddings of LLIconCtrl in this class has same value - Optional icon_ctrl_pad; - - Params() - : icon_ctrl_pad("icon_ctrl_pad", 1) - {} - }; - -protected: - friend class LLUICtrlFactory; - - LLCustomButtonIconCtrl(const Params& p) - : LLButton(p), - mIcon(NULL), - mIconAlignment(LLFontGL::HCENTER), - mIconCtrlPad(p.icon_ctrl_pad) - {} - -public: - - void updateLayout() - { - LLRect button_rect = getRect(); - LLRect icon_rect = mIcon->getRect(); - - S32 icon_size = button_rect.getHeight() - 2*mIconCtrlPad; - - switch(mIconAlignment) - { - case LLFontGL::LEFT: - icon_rect.setLeftTopAndSize(button_rect.mLeft + mIconCtrlPad, button_rect.mTop - mIconCtrlPad, - icon_size, icon_size); - setLeftHPad(icon_size + mIconCtrlPad * 2); - break; - case LLFontGL::HCENTER: - icon_rect.setLeftTopAndSize(button_rect.mRight - (button_rect.getWidth() + mIconCtrlPad - icon_size)/2, button_rect.mTop - mIconCtrlPad, - icon_size, icon_size); - setRightHPad(icon_size + mIconCtrlPad * 2); - break; - case LLFontGL::RIGHT: - icon_rect.setLeftTopAndSize(button_rect.mRight - mIconCtrlPad - icon_size, button_rect.mTop - mIconCtrlPad, - icon_size, icon_size); - setRightHPad(icon_size + mIconCtrlPad * 2); - break; - default: - break; - } - mIcon->setRect(icon_rect); - } - - void setIcon(LLIconCtrl* icon, LLFontGL::HAlign alignment = LLFontGL::LEFT) - { - if(icon) - { - if(mIcon) - { - removeChild(mIcon); - mIcon->die(); - } - mIcon = icon; - mIconAlignment = alignment; - - addChild(mIcon); - updateLayout(); - } - } - - LLIconCtrl* getIconCtrl() const - { - return mIcon; - } - -private: - LLIconCtrl* mIcon; - LLFontGL::HAlign mIconAlignment; - S32 mIconCtrlPad; -}; -//============================================================================ - -struct LLPlaceHolderPanel : public LLPanel -{ - // create dummy param block to register with "placeholder" nane - struct Params : public LLPanel::Params{}; - LLPlaceHolderPanel(const Params& p) : LLPanel(p) - {} -}; -static LLDefaultChildRegistry::Register r1("placeholder"); -static LLDefaultChildRegistry::Register r2("tab_container"); - -LLTabContainer::TabParams::TabParams() -: tab_top_image_unselected("tab_top_image_unselected"), - tab_top_image_selected("tab_top_image_selected"), - tab_top_image_flash("tab_top_image_flash"), - tab_bottom_image_unselected("tab_bottom_image_unselected"), - tab_bottom_image_selected("tab_bottom_image_selected"), - tab_bottom_image_flash("tab_bottom_image_flash"), - tab_left_image_unselected("tab_left_image_unselected"), - tab_left_image_selected("tab_left_image_selected"), - tab_left_image_flash("tab_left_image_flash") -{} - -LLTabContainer::Params::Params() -: tab_width("tab_width"), - tab_min_width("tab_min_width"), - tab_max_width("tab_max_width"), - tab_height("tab_height"), - label_pad_bottom("label_pad_bottom"), - label_pad_left("label_pad_left"), - tab_position("tab_position"), - hide_tabs("hide_tabs", false), - hide_scroll_arrows("hide_scroll_arrows", false), - tab_padding_right("tab_padding_right"), - first_tab("first_tab"), - middle_tab("middle_tab"), - last_tab("last_tab"), - use_custom_icon_ctrl("use_custom_icon_ctrl", false), - open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false), - enable_tabs_flashing("enable_tabs_flashing", false), - tabs_flashing_color("tabs_flashing_color"), - tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0), - use_ellipses("use_ellipses"), - font_halign("halign"), - use_tab_offset("use_tab_offset", false) -{} - -LLTabContainer::LLTabContainer(const LLTabContainer::Params& p) -: LLPanel(p), - mCurrentTabIdx(-1), - mTabsHidden(p.hide_tabs), - mScrolled(false), - mScrollPos(0), - mScrollPosPixels(0), - mMaxScrollPos(0), - mTitleBox(NULL), - mTopBorderHeight(LLPANEL_BORDER_WIDTH), - mLockedTabCount(0), - mMinTabWidth(0), - mMaxTabWidth(p.tab_max_width), - mTabHeight(p.tab_height), - mLabelPadBottom(p.label_pad_bottom), - mLabelPadLeft(p.label_pad_left), - mPrevArrowBtn(NULL), - mNextArrowBtn(NULL), - mIsVertical( p.tab_position == LEFT ), - mHideScrollArrows(p.hide_scroll_arrows), - // Horizontal Specific - mJumpPrevArrowBtn(NULL), - mJumpNextArrowBtn(NULL), - mRightTabBtnOffset(p.tab_padding_right), - mTotalTabWidth(0), - mTabPosition(p.tab_position), - mFontHalign(p.font_halign), - mFont(p.font), - mFirstTabParams(p.first_tab), - mMiddleTabParams(p.middle_tab), - mLastTabParams(p.last_tab), - mCustomIconCtrlUsed(p.use_custom_icon_ctrl), - mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop), - mTabIconCtrlPad(p.tab_icon_ctrl_pad), - mEnableTabsFlashing(p.enable_tabs_flashing), - mTabsFlashingColor(p.tabs_flashing_color), - mUseTabEllipses(p.use_ellipses), - mUseTabOffset(p.use_tab_offset) -{ - static LLUICachedControl tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0); - - mDragAndDropDelayTimer.stop(); - - if (p.tab_width.isProvided()) - { - mMinTabWidth = p.tab_width; - } - else if (!mIsVertical) - { - mMinTabWidth = p.tab_min_width; - } - else - { - // *HACK: support default min width for legacy vertical - // tab containers - mMinTabWidth = tabcntr_vert_tab_min_width; - } - - if (p.tabs_flashing_color.isProvided()) - { - mEnableTabsFlashing = true; - } - - initButtons( ); -} - -LLTabContainer::~LLTabContainer() -{ - std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); - mTabList.clear(); -} - -//virtual -void LLTabContainer::setValue(const LLSD& value) -{ - selectTab((S32) value.asInteger()); -} - -//virtual -void LLTabContainer::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLPanel::reshape( width, height, called_from_parent ); - updateMaxScrollPos(); -} - -//virtual -LLView* LLTabContainer::getChildView(const std::string& name, bool recurse) const -{ - tuple_list_t::const_iterator itor; - for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) - { - LLPanel *panel = (*itor)->mTabPanel; - if (panel->getName() == name) - { - return panel; - } - } - - if (recurse) - { - for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) - { - LLPanel *panel = (*itor)->mTabPanel; - LLView *child = panel->getChildView(name, recurse); - if (child) - { - return child; - } - } - } - return LLView::getChildView(name, recurse); -} - -//virtual -LLView* LLTabContainer::findChildView(const std::string& name, bool recurse) const -{ - tuple_list_t::const_iterator itor; - for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) - { - LLPanel *panel = (*itor)->mTabPanel; - if (panel->getName() == name) - { - return panel; - } - } - - if (recurse) - { - for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) - { - LLPanel *panel = (*itor)->mTabPanel; - LLView *child = panel->findChildView(name, recurse); - if (child) - { - return child; - } - } - } - return LLView::findChildView(name, recurse); -} - -bool LLTabContainer::addChild(LLView* view, S32 tab_group) -{ - LLPanel* panelp = dynamic_cast(view); - - if (panelp) - { - addTabPanel(TabPanelParams().panel(panelp).label(panelp->getLabel()).is_placeholder(dynamic_cast(view) != NULL)); - return true; - } - else - { - return LLUICtrl::addChild(view, tab_group); - } -} - -bool LLTabContainer::postBuild() -{ - selectFirstTab(); - - return true; -} - -// virtual -void LLTabContainer::draw() -{ - static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - static LLUICachedControl tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0); - static LLUICachedControl tabcntr_tab_h_pad ("UITabCntrTabHPad", 0); - static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); - static LLUICachedControl tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0); - S32 target_pixel_scroll = 0; - S32 cur_scroll_pos = getScrollPos(); - if (cur_scroll_pos > 0) - { - if (mIsVertical) - { - target_pixel_scroll = cur_scroll_pos * (BTN_HEIGHT + tabcntrv_pad); - } - else - { - S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - if (cur_scroll_pos == 0) - { - break; - } - - if( (*iter)->mVisible ) - target_pixel_scroll += (*iter)->mButton->getRect().getWidth(); - - cur_scroll_pos--; - } - - // Show part of the tab to the left of what is fully visible - target_pixel_scroll -= tabcntr_tab_partial_width; - // clamp so that rightmost tab never leaves right side of screen - target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll); - } - } - - setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLSmoothInterpolation::getInterpolant(0.08f))); - - bool has_scroll_arrows = !mHideScrollArrows && !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0)); - if (!mIsVertical) - { - mJumpPrevArrowBtn->setVisible( has_scroll_arrows ); - mJumpNextArrowBtn->setVisible( has_scroll_arrows ); - } - mPrevArrowBtn->setVisible( has_scroll_arrows ); - mNextArrowBtn->setVisible( has_scroll_arrows ); - - S32 left = 0, top = 0; - if (mIsVertical) - { - top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? tabcntrv_arrow_btn_size : 0); - top += getScrollPosPixels(); - } - else - { - // Set the leftmost position of the tab buttons. - left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (tabcntr_arrow_btn_size * 2) : tabcntr_tab_h_pad); - left -= getScrollPosPixels(); - } - - // Hide all the buttons - if (getTabsHidden()) - { - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( false ); - } - } - - { - LLRect clip_rect = getLocalRect(); - clip_rect.mLeft+=(LLPANEL_BORDER_WIDTH + 2); - clip_rect.mRight-=(LLPANEL_BORDER_WIDTH + 2); - LLLocalClipRect clip(clip_rect); - LLPanel::draw(); - } - - // if tabs are hidden, don't draw them and leave them in the invisible state - if (!getTabsHidden()) - { - // Show all the buttons - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( true ); - } - - S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos(); - S32 idx = 0; - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - - if( !tuple->mVisible ) - { - tuple->mButton->setVisible( false ); - continue; - } - - tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0, - top ? top - tuple->mButton->getRect().mTop : 0 ); - if (top) top -= BTN_HEIGHT + tabcntrv_pad; - if (left) left += tuple->mButton->getRect().getWidth(); - - if (!mIsVertical) - { - if( idx < getScrollPos() ) - { - if( tuple->mButton->getFlashing() ) - { - mPrevArrowBtn->setFlashing( true ); - } - } - else if( max_scroll_visible < idx ) - { - if( tuple->mButton->getFlashing() ) - { - mNextArrowBtn->setFlashing( true ); - } - } - } - - idx++; - } - - - if( mIsVertical && has_scroll_arrows ) - { - // Redraw the arrows so that they appears on top. - gGL.pushUIMatrix(); - gGL.translateUI((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f); - mPrevArrowBtn->draw(); - gGL.popUIMatrix(); - - gGL.pushUIMatrix(); - gGL.translateUI((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f); - mNextArrowBtn->draw(); - gGL.popUIMatrix(); - } - } - - mPrevArrowBtn->setFlashing(false); - mNextArrowBtn->setFlashing(false); -} - - -// virtual -bool LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - bool handled = false; - bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); - - if (has_scroll_arrows) - { - if (mJumpPrevArrowBtn&& mJumpPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; - handled = mJumpPrevArrowBtn->handleMouseDown(local_x, local_y, mask); - } - else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; - handled = mJumpNextArrowBtn->handleMouseDown(local_x, local_y, mask); - } - else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mPrevArrowBtn->getRect().mBottom; - handled = mPrevArrowBtn->handleMouseDown(local_x, local_y, mask); - } - else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mNextArrowBtn->getRect().mLeft; - S32 local_y = y - mNextArrowBtn->getRect().mBottom; - handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask); - } - } - if (!handled) - { - handled = LLPanel::handleMouseDown( x, y, mask ); - } - - S32 tab_count = getTabCount(); - if (tab_count > 0 && !getTabsHidden()) - { - LLTabTuple* firsttuple = getTab(0); - LLRect tab_rect; - if (mIsVertical) - { - tab_rect = LLRect(firsttuple->mButton->getRect().mLeft, - has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop, - firsttuple->mButton->getRect().mRight, - has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom ); - } - else - { - tab_rect = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft, - firsttuple->mButton->getRect().mTop, - has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight, - firsttuple->mButton->getRect().mBottom ); - } - if( tab_rect.pointInRect( x, y ) ) - { - S32 index = getCurrentPanelIndex(); - index = llclamp(index, 0, tab_count-1); - LLButton* tab_button = getTab(index)->mButton; - gFocusMgr.setMouseCapture(this); - tab_button->setFocus(true); - mMouseDownTimer.start(); - } - } - if (handled) { - // Note: May need to also capture local coords right here ? - LLViewerEventRecorder::instance().update_xui(getPathname( )); - } - - return handled; -} - -// virtual -bool LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) -{ - bool handled = false; - bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); - - if (has_scroll_arrows) - { - if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; - handled = mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); - } - else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; - handled = mJumpNextArrowBtn->handleHover(local_x, local_y, mask); - } - else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mPrevArrowBtn->getRect().mBottom; - handled = mPrevArrowBtn->handleHover(local_x, local_y, mask); - } - else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mNextArrowBtn->getRect().mLeft; - S32 local_y = y - mNextArrowBtn->getRect().mBottom; - handled = mNextArrowBtn->handleHover(local_x, local_y, mask); - } - } - if (!handled) - { - handled = LLPanel::handleHover(x, y, mask); - } - - F32 drag_delay = 0.25f; // filter out clicks from dragging - if (mMouseDownTimer.getElapsedTimeF32() > drag_delay) - { - commitHoveredButton(x, y); - } - return handled; -} - -// virtual -bool LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) -{ - bool handled = false; - bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); - - S32 local_x = x - getRect().mLeft; - S32 local_y = y - getRect().mBottom; - - if (has_scroll_arrows) - { - if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) - { - local_x = x - mJumpPrevArrowBtn->getRect().mLeft; - local_y = y - mJumpPrevArrowBtn->getRect().mBottom; - handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask); - } - else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) - { - local_x = x - mJumpNextArrowBtn->getRect().mLeft; - local_y = y - mJumpNextArrowBtn->getRect().mBottom; - handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask); - } - else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) - { - local_x = x - mPrevArrowBtn->getRect().mLeft; - local_y = y - mPrevArrowBtn->getRect().mBottom; - handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask); - } - else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) - { - local_x = x - mNextArrowBtn->getRect().mLeft; - local_y = y - mNextArrowBtn->getRect().mBottom; - handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask); - } - } - if (!handled) - { - handled = LLPanel::handleMouseUp( x, y, mask ); - } - - commitHoveredButton(x, y); - mMouseDownTimer.stop(); - LLPanel* cur_panel = getCurrentPanel(); - if (hasMouseCapture()) - { - if (cur_panel) - { - if (!cur_panel->focusFirstItem(false)) - { - // if nothing in the panel gets focus, make sure the new tab does - // otherwise the last tab might keep focus - getTab(getCurrentPanelIndex())->mButton->setFocus(true); - } - } - gFocusMgr.setMouseCapture(NULL); - } - if (handled) { - // Note: may need to capture local coords here - LLViewerEventRecorder::instance().update_xui(getPathname( )); - } - return handled; -} - -// virtual -bool LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask) -{ - static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - bool handled = LLPanel::handleToolTip( x, y, mask); - if (!handled && getTabCount() > 0 && !getTabsHidden()) - { - LLTabTuple* firsttuple = getTab(0); - - bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); - LLRect clip; - if (mIsVertical) - { - clip = LLRect(firsttuple->mButton->getRect().mLeft, - has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop, - firsttuple->mButton->getRect().mRight, - has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom ); - } - else - { - clip = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft, - firsttuple->mButton->getRect().mTop, - has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight, - firsttuple->mButton->getRect().mBottom ); - } - - if( clip.pointInRect( x, y ) ) - { - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLButton* tab_button = (*iter)->mButton; - if (!tab_button->getVisible()) continue; - S32 local_x = x - tab_button->getRect().mLeft; - S32 local_y = y - tab_button->getRect().mBottom; - handled = tab_button->handleToolTip(local_x, local_y, mask); - if( handled ) - { - break; - } - } - } - } - return handled; -} - -// virtual -bool LLTabContainer::handleKeyHere(KEY key, MASK mask) -{ - bool handled = false; - if (key == KEY_LEFT && mask == MASK_ALT) - { - selectPrevTab(); - handled = true; - } - else if (key == KEY_RIGHT && mask == MASK_ALT) - { - selectNextTab(); - handled = true; - } - - if (handled) - { - if (getCurrentPanel()) - { - getCurrentPanel()->setFocus(true); - } - } - - if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel())) - { - // if child has focus, but not the current panel, focus is on a button - if (mIsVertical) - { - switch(key) - { - case KEY_UP: - selectPrevTab(); - handled = true; - break; - case KEY_DOWN: - selectNextTab(); - handled = true; - break; - case KEY_LEFT: - handled = true; - break; - case KEY_RIGHT: - if (getTabPosition() == LEFT && getCurrentPanel()) - { - getCurrentPanel()->setFocus(true); - } - handled = true; - break; - default: - break; - } - } - else - { - switch(key) - { - case KEY_UP: - if (getTabPosition() == BOTTOM && getCurrentPanel()) - { - getCurrentPanel()->setFocus(true); - } - handled = true; - break; - case KEY_DOWN: - if (getTabPosition() == TOP && getCurrentPanel()) - { - getCurrentPanel()->setFocus(true); - } - handled = true; - break; - case KEY_LEFT: - selectPrevTab(); - handled = true; - break; - case KEY_RIGHT: - selectNextTab(); - handled = true; - break; - default: - break; - } - } - } - return handled; -} - -// virtual -bool LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string &tooltip) -{ - bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); - - if(mOpenTabsOnDragAndDrop && !getTabsHidden()) - { - // In that case, we'll open the hovered tab while dragging and dropping items. - // This allows for drilling through tabs. - if (mDragAndDropDelayTimer.getStarted()) - { - if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME) - { - if (has_scroll_arrows) - { - if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; - mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); - } - if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; - mJumpNextArrowBtn->handleHover(local_x, local_y, mask); - } - if (mPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mPrevArrowBtn->getRect().mBottom; - mPrevArrowBtn->handleHover(local_x, local_y, mask); - } - else if (mNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mNextArrowBtn->getRect().mLeft; - S32 local_y = y - mNextArrowBtn->getRect().mBottom; - mNextArrowBtn->handleHover(local_x, local_y, mask); - } - } - - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( true ); - S32 local_x = x - tuple->mButton->getRect().mLeft; - S32 local_y = y - tuple->mButton->getRect().mBottom; - if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible()) - { - tuple->mButton->onCommit(); - } - } - // Stop the timer whether successful or not. Don't let it run forever. - mDragAndDropDelayTimer.stop(); - } - } - else - { - // Start a timer so we don't open tabs as soon as we hover on them - mDragAndDropDelayTimer.start(); - } - } - - return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip); -} - -void LLTabContainer::addTabPanel(LLPanel* panelp) -{ - addTabPanel(TabPanelParams().panel(panelp)); -} - -// function to update images -void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos) -{ - if (tuple && tuple->mButton) - { - if (pos == LLTabContainer::TOP) - { - tuple->mButton->setImageUnselected(static_cast(params.tab_top_image_unselected)); - tuple->mButton->setImageSelected(static_cast(params.tab_top_image_selected)); - tuple->mButton->setImageFlash(static_cast(params.tab_top_image_flash)); - } - else if (pos == LLTabContainer::BOTTOM) - { - tuple->mButton->setImageUnselected(static_cast(params.tab_bottom_image_unselected)); - tuple->mButton->setImageSelected(static_cast(params.tab_bottom_image_selected)); - tuple->mButton->setImageFlash(static_cast(params.tab_bottom_image_flash)); - } - else if (pos == LLTabContainer::LEFT) - { - tuple->mButton->setImageUnselected(static_cast(params.tab_left_image_unselected)); - tuple->mButton->setImageSelected(static_cast(params.tab_left_image_selected)); - tuple->mButton->setImageFlash(static_cast(params.tab_left_image_flash)); - } - } -} - -void LLTabContainer::addTabPanel(const TabPanelParams& panel) -{ - LLPanel* child = panel.panel(); - - llassert(child); - if (!child) return; - - const std::string& label = panel.label.isProvided() - ? panel.label() - : panel.panel()->getLabel(); - bool select = panel.select_tab(); - S32 indent = panel.indent(); - bool placeholder = panel.is_placeholder; - eInsertionPoint insertion_point = panel.insert_at(); - - static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - static LLUICachedControl tabcntr_button_panel_overlap ("UITabCntrButtonPanelOverlap", 0); - static LLUICachedControl tab_padding ("UITabPadding", 0); - if (child->getParent() == this) - { - // already a child of mine - return; - } - - // Store the original label for possible xml export. - child->setLabel(label); - std::string trimmed_label = label; - LLStringUtil::trim(trimmed_label); - - S32 button_width = mMinTabWidth; - if (!mIsVertical) - { - button_width = llclamp(mFont->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth); - } - - // Tab panel - S32 tab_panel_top; - S32 tab_panel_bottom; - if (!getTabsHidden()) - { - if( getTabPosition() == LLTabContainer::TOP ) - { - S32 tab_height = mIsVertical ? BTN_HEIGHT : mTabHeight; - tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - tabcntr_button_panel_overlap); - tab_panel_bottom = LLPANEL_BORDER_WIDTH; - } - else - { - tab_panel_top = getRect().getHeight() - getTopBorderHeight(); - tab_panel_bottom = (mTabHeight - tabcntr_button_panel_overlap); // Run to the edge, covering up the border - } - } - else - { - // Skip tab button space if tabs are invisible (EXT-576) - tab_panel_top = getRect().getHeight(); - tab_panel_bottom = LLPANEL_BORDER_WIDTH; - } - - LLRect tab_panel_rect; - if (!getTabsHidden() && mIsVertical) - { - tab_panel_rect = LLRect(mMinTabWidth + mRightTabBtnOffset + (LLPANEL_BORDER_WIDTH * 2) + tabcntrv_pad, - getRect().getHeight() - LLPANEL_BORDER_WIDTH, - getRect().getWidth() - LLPANEL_BORDER_WIDTH, - LLPANEL_BORDER_WIDTH); - } - else - { - S32 left_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 3 : LLPANEL_BORDER_WIDTH; - S32 right_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 2 : LLPANEL_BORDER_WIDTH; - tab_panel_rect = LLRect(left_offset, tab_panel_top, getRect().getWidth() - right_offset, tab_panel_bottom); - } - child->setFollowsAll(); - child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom); - child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), true ); - // add this child later - - child->setVisible( false ); // Will be made visible when selected - - mTotalTabWidth += button_width; - - // Tab button - LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw(). - LLUIImage* tab_img = NULL; - LLUIImage* tab_selected_img = NULL; - S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel - - if (mIsVertical) - { - btn_rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor - (getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * getTabCount()), - mMinTabWidth, - BTN_HEIGHT); - } - else if( getTabPosition() == LLTabContainer::TOP ) - { - btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, mTabHeight); - tab_img = mMiddleTabParams.tab_top_image_unselected; - tab_selected_img = mMiddleTabParams.tab_top_image_selected; - } - else - { - btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, mTabHeight); - tab_img = mMiddleTabParams.tab_bottom_image_unselected; - tab_selected_img = mMiddleTabParams.tab_bottom_image_selected; - } - - LLTextBox* textbox = NULL; - LLButton* btn = NULL; - LLCustomButtonIconCtrl::Params custom_btn_params; - { - custom_btn_params.icon_ctrl_pad(mTabIconCtrlPad); - } - LLButton::Params normal_btn_params; - - if (placeholder) - { - btn_rect.translate(0, -6); // *TODO: make configurable - LLTextBox::Params params; - params.name(trimmed_label); - params.rect(btn_rect); - params.initial_value(trimmed_label); - params.font(mFont); - textbox = LLUICtrlFactory::create (params); - - LLButton::Params p; - p.name("placeholder"); - btn = LLUICtrlFactory::create(p); - } - else - { - LLButton::Params& p = (mCustomIconCtrlUsed ? custom_btn_params : normal_btn_params); - - p.rect(btn_rect); - p.font(mFont); - p.font_halign = mFontHalign; - p.label(trimmed_label); - p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child)); - if (indent) - { - p.pad_left(indent); - } - p.pad_bottom( mLabelPadBottom ); - p.scale_image(true); - p.tab_stop(false); - p.label_shadow(false); - p.follows.flags = FOLLOWS_LEFT; - - if (mIsVertical) - { - p.name("vtab_"+std::string(child->getName())); - p.image_unselected(mMiddleTabParams.tab_left_image_unselected); - p.image_selected(mMiddleTabParams.tab_left_image_selected); - p.follows.flags = p.follows.flags() | FOLLOWS_TOP; - } - else - { - p.name("htab_"+std::string(child->getName())); - p.visible(false); - p.image_unselected(tab_img); - p.image_selected(tab_selected_img); - p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM); - // Try to squeeze in a bit more text - p.pad_left( mLabelPadLeft ); - p.pad_right(2); - } - - // inits flash timer - p.button_flash_enable = mEnableTabsFlashing; - p.flash_color = mTabsFlashingColor; - - // *TODO : It seems wrong not to use p in both cases considering the way p is initialized - if (mCustomIconCtrlUsed) - { - btn = LLUICtrlFactory::create(custom_btn_params); - } - else - { - btn = LLUICtrlFactory::create(p); - } - } - - LLTabTuple* tuple = new LLTabTuple( this, child, btn, textbox ); - insertTuple( tuple, insertion_point ); - - // if new tab was added as a first or last tab, update button image - // and update button image of any tab it may have affected - if (tuple == mTabList.front()) - { - update_images(tuple, mFirstTabParams, getTabPosition()); - - if (mTabList.size() == 2) - { - update_images(mTabList[1], mLastTabParams, getTabPosition()); - } - else if (mTabList.size() > 2) - { - update_images(mTabList[1], mMiddleTabParams, getTabPosition()); - } - } - else if (tuple == mTabList.back()) - { - update_images(tuple, mLastTabParams, getTabPosition()); - - if (mTabList.size() > 2) - { - update_images(mTabList[mTabList.size()-2], mMiddleTabParams, getTabPosition()); - } - } - - //Don't add button and textbox if tab buttons are invisible(EXT - 576) - if (!getTabsHidden()) - { - if (textbox) - { - addChild( textbox, 0 ); - } - if (btn) - { - addChild( btn, 0 ); - } - } - else - { - if (textbox) - { - LLUICtrl::addChild(textbox, 0); - } - if (btn) - { - LLUICtrl::addChild(btn, 0); - } - } - - if (child) - { - LLUICtrl::addChild(child, 1); - } - - sendChildToFront(mPrevArrowBtn); - sendChildToFront(mNextArrowBtn); - sendChildToFront(mJumpPrevArrowBtn); - sendChildToFront(mJumpNextArrowBtn); - - updateMaxScrollPos(); - - if( select ) - { - selectLastTab(); - mScrollPos = mMaxScrollPos; - } - -} - -void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label) -{ - addTabPanel(TabPanelParams().panel(child).label(label).is_placeholder(true)); -} - -void LLTabContainer::removeTabPanel(LLPanel* child) -{ - static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - if (mIsVertical) - { - // Fix-up button sizes - S32 tab_count = 0; - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - LLRect rect; - rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor - (getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * (tab_count)), - mMinTabWidth, - BTN_HEIGHT); - if (tuple->mPlaceholderText) - { - tuple->mPlaceholderText->setRect(rect); - } - else - { - tuple->mButton->setRect(rect); - } - tab_count++; - } - } - else - { - // Adjust the total tab width. - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - if( tuple->mTabPanel == child ) - { - mTotalTabWidth -= tuple->mButton->getRect().getWidth(); - break; - } - } - } - - bool has_focus = gFocusMgr.childHasKeyboardFocus(this); - - // If the tab being deleted is the selected one, select a different tab. - for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - if( tuple->mTabPanel == child ) - { - // update tab button images if removing the first or last tab - if ((tuple == mTabList.front()) && (mTabList.size() > 1)) - { - update_images(mTabList[1], mFirstTabParams, getTabPosition()); - } - else if ((tuple == mTabList.back()) && (mTabList.size() > 2)) - { - update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition()); - } - - if (!getTabsHidden()) - { - // We need to remove tab buttons only if the tabs are not hidden. - removeChild( tuple->mButton ); - } - delete tuple->mButton; - tuple->mButton = NULL; - - removeChild( tuple->mTabPanel ); -// delete tuple->mTabPanel; - tuple->mTabPanel = NULL; - - mTabList.erase( iter ); - delete tuple; - - break; - } - } - - // make sure we don't have more locked tabs than we have tabs - mLockedTabCount = llmin(getTabCount(), mLockedTabCount); - - if (mCurrentTabIdx >= (S32)mTabList.size()) - { - mCurrentTabIdx = mTabList.size()-1; - } - selectTab(mCurrentTabIdx); - if (has_focus) - { - LLPanel* panelp = getPanelByIndex(mCurrentTabIdx); - if (panelp) - { - panelp->setFocus(true); - } - } - - updateMaxScrollPos(); -} - -void LLTabContainer::lockTabs(S32 num_tabs) -{ - // count current tabs or use supplied value and ensure no new tabs get - // inserted between them - mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount(); -} - -void LLTabContainer::unlockTabs() -{ - mLockedTabCount = 0; -} - -void LLTabContainer::enableTabButton(S32 which, bool enable) -{ - if (which >= 0 && which < (S32)mTabList.size()) - { - mTabList[which]->mButton->setEnabled(enable); - } - // Stop the DaD timer as it might run forever - // enableTabButton() is typically called on refresh and draw when anything changed - // in the tab container so it's a good time to reset that. - mDragAndDropDelayTimer.stop(); -} - -void LLTabContainer::deleteAllTabs() -{ - // Remove all the tab buttons and delete them. Also, unlink all the child panels. - for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - - removeChild( tuple->mButton ); - delete tuple->mButton; - tuple->mButton = NULL; - - removeChild( tuple->mTabPanel ); -// delete tuple->mTabPanel; - tuple->mTabPanel = NULL; - } - - // Actually delete the tuples themselves - std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); - mTabList.clear(); - - // And there isn't a current tab any more - mCurrentTabIdx = -1; -} - -LLPanel* LLTabContainer::getCurrentPanel() -{ - if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size()) - { - return mTabList[mCurrentTabIdx]->mTabPanel; - } - return NULL; -} - -S32 LLTabContainer::getCurrentPanelIndex() -{ - return mCurrentTabIdx; -} - -S32 LLTabContainer::getTabCount() -{ - return mTabList.size(); -} - -LLPanel* LLTabContainer::getPanelByIndex(S32 index) -{ - if (index >= 0 && index < (S32)mTabList.size()) - { - return mTabList[index]->mTabPanel; - } - return NULL; -} - -S32 LLTabContainer::getIndexForPanel(LLPanel* panel) -{ - for (S32 index = 0; index < (S32)mTabList.size(); index++) - { - if (mTabList[index]->mTabPanel == panel) - { - return index; - } - } - return -1; -} - -S32 LLTabContainer::getPanelIndexByTitle(const std::string& title) -{ - for (S32 index = 0 ; index < (S32)mTabList.size(); index++) - { - if (title == mTabList[index]->mButton->getLabelSelected()) - { - return index; - } - } - return -1; -} - -LLPanel* LLTabContainer::getPanelByName(const std::string& name) -{ - for (S32 index = 0 ; index < (S32)mTabList.size(); index++) - { - LLPanel *panel = mTabList[index]->mTabPanel; - if (name == panel->getName()) - { - return panel; - } - } - return NULL; -} - -// Change the name of the button for the current tab. -void LLTabContainer::setCurrentTabName(const std::string& name) -{ - // Might not have a tab selected - if (mCurrentTabIdx < 0) return; - - mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name); - mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name); -} - -void LLTabContainer::selectFirstTab() -{ - selectTab( 0 ); -} - - -void LLTabContainer::selectLastTab() -{ - selectTab( mTabList.size()-1 ); -} - -void LLTabContainer::selectNextTab() -{ - if (mTabList.size() == 0) - { - return; - } - - bool tab_has_focus = false; - if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus()) - { - tab_has_focus = true; - } - S32 idx = mCurrentTabIdx+1; - if (idx >= (S32)mTabList.size()) - idx = 0; - while (!selectTab(idx) && idx != mCurrentTabIdx) - { - idx = (idx + 1 ) % (S32)mTabList.size(); - } - - if (tab_has_focus) - { - mTabList[idx]->mButton->setFocus(true); - } -} - -void LLTabContainer::selectPrevTab() -{ - bool tab_has_focus = false; - if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus()) - { - tab_has_focus = true; - } - S32 idx = mCurrentTabIdx-1; - if (idx < 0) - idx = mTabList.size()-1; - while (!selectTab(idx) && idx != mCurrentTabIdx) - { - idx = idx - 1; - if (idx < 0) - idx = mTabList.size()-1; - } - if (tab_has_focus) - { - mTabList[idx]->mButton->setFocus(true); - } -} - -bool LLTabContainer::selectTabPanel(LLPanel* child) -{ - S32 idx = 0; - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - if( tuple->mTabPanel == child ) - { - return selectTab( idx ); - } - idx++; - } - return false; -} - -bool LLTabContainer::selectTab(S32 which) -{ - if (which >= getTabCount() || which < 0) - return false; - - LLTabTuple* selected_tuple = getTab(which); - if (!selected_tuple) - return false; - - LLSD cbdata; - if (selected_tuple->mTabPanel) - cbdata = selected_tuple->mTabPanel->getName(); - - bool result = false; - if (!mValidateSignal || (*mValidateSignal)(this, cbdata)) - { - result = setTab(which); - if (result && mCommitSignal) - { - (*mCommitSignal)(this, cbdata); - } - } - - return result; -} - -// private -bool LLTabContainer::setTab(S32 which) -{ - static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); - LLTabTuple* selected_tuple = getTab(which); - if (!selected_tuple) - { - return false; - } - - bool is_visible = false; - if( selected_tuple->mButton->getEnabled() && selected_tuple->mVisible ) - { - setCurrentPanelIndex(which); - - S32 i = 0; - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - bool is_selected = ( tuple == selected_tuple ); - // Although the selected tab must be complete, we may have hollow LLTabTuple tucked in the list - if (tuple && tuple->mButton) - { - tuple->mButton->setUseEllipses(mUseTabEllipses); - tuple->mButton->setHAlign(mFontHalign); - tuple->mButton->setToggleState( is_selected ); - // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs - tuple->mButton->setTabStop( is_selected ); - } - if (tuple && tuple->mTabPanel) - { - tuple->mTabPanel->setVisible( is_selected ); - //tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here. - } - - if (is_selected) - { - LLUIUsage::instance().logPanel(tuple->mTabPanel->getName()); - - // Make sure selected tab is within scroll region - if (mIsVertical) - { - S32 num_visible = getTabCount() - getMaxScrollPos(); - if( i >= getScrollPos() && i <= getScrollPos() + num_visible) - { - setCurrentPanelIndex(which); - is_visible = true; - } - else - { - is_visible = false; - } - } - else if (!mHideScrollArrows && getMaxScrollPos() > 0) - { - if( i < getScrollPos() ) - { - setScrollPos(i); - } - else - { - S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); - S32 running_tab_width = (tuple && tuple->mButton ? tuple->mButton->getRect().getWidth() : 0); - S32 j = i - 1; - S32 min_scroll_pos = i; - if (running_tab_width < available_width_with_arrows) - { - while (j >= 0) - { - LLTabTuple* other_tuple = getTab(j); - running_tab_width += (other_tuple && other_tuple->mButton ? other_tuple->mButton->getRect().getWidth() : 0); - if (running_tab_width > available_width_with_arrows) - { - break; - } - j--; - } - min_scroll_pos = j + 1; - } - setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i)); - setScrollPos(llmin(getScrollPos(), getMaxScrollPos())); - } - is_visible = true; - } - else - { - is_visible = true; - } - } - i++; - } - } - if (mIsVertical && getCurrentPanelIndex() >= 0) - { - LLTabTuple* tuple = getTab(getCurrentPanelIndex()); - tuple->mTabPanel->setVisible( true ); - tuple->mButton->setToggleState( true ); - } - return is_visible; -} - -bool LLTabContainer::selectTabByName(const std::string& name) -{ - LLPanel* panel = getPanelByName(name); - if (!panel) - { - LL_WARNS() << "LLTabContainer::selectTabByName(" << name << ") failed" << LL_ENDL; - return false; - } - - bool result = selectTabPanel(panel); - return result; -} - -bool LLTabContainer::getTabPanelFlashing(LLPanel *child) -{ - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) - { - return tuple->mButton->getFlashing(); - } - return false; -} - -void LLTabContainer::setTabPanelFlashing(LLPanel* child, bool state ) -{ - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) - { - tuple->mButton->setFlashing( state ); - } -} - -void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color) -{ - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) - { - tuple->mButton->setImageOverlay(image_name, LLFontGL::LEFT, color); - reshapeTuple(tuple); - } -} - -void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color) -{ - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) - { - tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color); - reshapeTuple(tuple); - } -} - -void LLTabContainer::setTabImage(LLPanel* child, LLIconCtrl* icon) -{ - LLTabTuple* tuple = getTabByPanel(child); - LLCustomButtonIconCtrl* button; - bool hasButton = false; - - if(tuple) - { - button = dynamic_cast(tuple->mButton); - if(button) - { - hasButton = true; - button->setIcon(icon); - reshapeTuple(tuple); - } - } - - if (!hasButton && (icon != NULL)) - { - // It was assumed that the tab's button would take ownership of the icon pointer. - // But since the tab did not have a button, kill the icon to prevent the memory - // leak. - icon->die(); - } -} - -void LLTabContainer::reshapeTuple(LLTabTuple* tuple) -{ - static LLUICachedControl tab_padding ("UITabPadding", 0); - - if (!mIsVertical) - { - S32 image_overlay_width = 0; - - if(mCustomIconCtrlUsed) - { - LLCustomButtonIconCtrl* button = dynamic_cast(tuple->mButton); - LLIconCtrl* icon_ctrl = button ? button->getIconCtrl() : NULL; - image_overlay_width = icon_ctrl ? icon_ctrl->getRect().getWidth() : 0; - } - else - { - image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? - tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0; - } - // remove current width from total tab strip width - mTotalTabWidth -= tuple->mButton->getRect().getWidth(); - - tuple->mPadding = image_overlay_width; - - tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), - tuple->mButton->getRect().getHeight()); - // add back in button width to total tab strip width - mTotalTabWidth += tuple->mButton->getRect().getWidth(); - - // tabs have changed size, might need to scroll to see current tab - updateMaxScrollPos(); - } -} - -void LLTabContainer::setTitle(const std::string& title) -{ - if (mTitleBox) - { - mTitleBox->setText( title ); - } -} - -const std::string LLTabContainer::getPanelTitle(S32 index) -{ - if (index >= 0 && index < (S32)mTabList.size()) - { - LLButton* tab_button = mTabList[index]->mButton; - return tab_button->getLabelSelected(); - } - return LLStringUtil::null; -} - -void LLTabContainer::setTopBorderHeight(S32 height) -{ - mTopBorderHeight = height; -} - -S32 LLTabContainer::getTopBorderHeight() const -{ - return mTopBorderHeight; -} - -void LLTabContainer::setRightTabBtnOffset(S32 offset) -{ - mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 ); - mRightTabBtnOffset = offset; - updateMaxScrollPos(); -} - -void LLTabContainer::setPanelTitle(S32 index, const std::string& title) -{ - static LLUICachedControl tab_padding ("UITabPadding", 0); - - if (index >= 0 && index < getTabCount()) - { - LLTabTuple* tuple = getTab(index); - LLButton* tab_button = tuple->mButton; - const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall(); - mTotalTabWidth -= tab_button->getRect().getWidth(); - tab_button->reshape(llclamp(fontp->getWidth(title) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight()); - mTotalTabWidth += tab_button->getRect().getWidth(); - tab_button->setLabelSelected(title); - tab_button->setLabelUnselected(title); - } - updateMaxScrollPos(); -} - - -void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel ) -{ - LLTabTuple* tuple = getTabByPanel(panel); - selectTabPanel( panel ); - - if (tuple) - { - tuple->mTabPanel->setFocus(true); - } -} - -void LLTabContainer::onNextBtn( const LLSD& data ) -{ - if (!mScrolled) - { - scrollNext(); - } - mScrolled = false; - - if(mCurrentTabIdx < mTabList.size()-1) - { - selectNextTab(); - } -} - -void LLTabContainer::onNextBtnHeld( const LLSD& data ) -{ - if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) - { - mScrollTimer.reset(); - scrollNext(); - - if(mCurrentTabIdx < mTabList.size()-1) - { - selectNextTab(); - } - mScrolled = true; - } -} - -void LLTabContainer::onPrevBtn( const LLSD& data ) -{ - if (!mScrolled) - { - scrollPrev(); - } - mScrolled = false; - - if(mCurrentTabIdx > 0) - { - selectPrevTab(); - } -} - -void LLTabContainer::onJumpFirstBtn( const LLSD& data ) -{ - mScrollPos = 0; -} - -void LLTabContainer::onJumpLastBtn( const LLSD& data ) -{ - mScrollPos = mMaxScrollPos; -} - -void LLTabContainer::onPrevBtnHeld( const LLSD& data ) -{ - if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) - { - mScrollTimer.reset(); - scrollPrev(); - - if(mCurrentTabIdx > 0) - { - selectPrevTab(); - } - mScrolled = true; - } -} - -// private - -void LLTabContainer::initButtons() -{ - // Hack: - if (getRect().getHeight() == 0 || mPrevArrowBtn) - { - return; // Don't have a rect yet or already got called - } - - if (mIsVertical) - { - static LLUICachedControl tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0); - // Left and right scroll arrows (for when there are too many tabs to show all at once). - S32 btn_top = getRect().getHeight(); - S32 btn_top_lower = getRect().mBottom+tabcntrv_arrow_btn_size; - - LLRect up_arrow_btn_rect; - up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size ); - - LLRect down_arrow_btn_rect; - down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size ); - - LLButton::Params prev_btn_params; - prev_btn_params.name(std::string("Up Arrow")); - prev_btn_params.rect(up_arrow_btn_rect); - prev_btn_params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT); - prev_btn_params.image_unselected.name("scrollbutton_up_out_blue.tga"); - prev_btn_params.image_selected.name("scrollbutton_up_in_blue.tga"); - prev_btn_params.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2)); - mPrevArrowBtn = LLUICtrlFactory::create(prev_btn_params); - - LLButton::Params next_btn_params; - next_btn_params.name(std::string("Down Arrow")); - next_btn_params.rect(down_arrow_btn_rect); - next_btn_params.follows.flags(FOLLOWS_BOTTOM | FOLLOWS_LEFT); - next_btn_params.image_unselected.name("scrollbutton_down_out_blue.tga"); - next_btn_params.image_selected.name("scrollbutton_down_in_blue.tga"); - next_btn_params.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2)); - mNextArrowBtn = LLUICtrlFactory::create(next_btn_params); - } - else // Horizontal - { - static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); - S32 arrow_fudge = 1; // match new art better - - // Left and right scroll arrows (for when there are too many tabs to show all at once). - S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : tabcntr_arrow_btn_size + 1; - - LLRect left_arrow_btn_rect; - left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+tabcntr_arrow_btn_size, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight ); - - LLRect jump_left_arrow_btn_rect; - jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight ); - - S32 right_pad = tabcntr_arrow_btn_size + LLPANEL_BORDER_WIDTH + 1; - - LLRect right_arrow_btn_rect; - right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - tabcntr_arrow_btn_size, - btn_top + arrow_fudge, - tabcntr_arrow_btn_size, mTabHeight ); - - - LLRect jump_right_arrow_btn_rect; - jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad, - btn_top + arrow_fudge, - tabcntr_arrow_btn_size, mTabHeight ); - - LLButton::Params p; - p.name(std::string("Jump Left Arrow")); - p.image_unselected.name("jump_left_out.tga"); - p.image_selected.name("jump_left_in.tga"); - p.click_callback.function(boost::bind(&LLTabContainer::onJumpFirstBtn, this, _2)); - p.rect(jump_left_arrow_btn_rect); - p.follows.flags(FOLLOWS_LEFT); - - mJumpPrevArrowBtn = LLUICtrlFactory::create(p); - - p = LLButton::Params(); - p.name(std::string("Left Arrow")); - p.rect(left_arrow_btn_rect); - p.follows.flags(FOLLOWS_LEFT); - p.image_unselected.name("scrollbutton_left_out_blue.tga"); - p.image_selected.name("scrollbutton_left_in_blue.tga"); - p.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2)); - p.mouse_held_callback.function(boost::bind(&LLTabContainer::onPrevBtnHeld, this, _2)); - - mPrevArrowBtn = LLUICtrlFactory::create(p); - - p = LLButton::Params(); - p.name(std::string("Jump Right Arrow")); - p.rect(jump_right_arrow_btn_rect); - p.follows.flags(FOLLOWS_RIGHT); - p.image_unselected.name("jump_right_out.tga"); - p.image_selected.name("jump_right_in.tga"); - p.click_callback.function(boost::bind(&LLTabContainer::onJumpLastBtn, this, _2)); - - mJumpNextArrowBtn = LLUICtrlFactory::create(p); - - p = LLButton::Params(); - p.name(std::string("Right Arrow")); - p.rect(right_arrow_btn_rect); - p.follows.flags(FOLLOWS_RIGHT); - p.image_unselected.name("scrollbutton_right_out_blue.tga"); - p.image_selected.name("scrollbutton_right_in_blue.tga"); - p.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2)); - p.mouse_held_callback.function(boost::bind(&LLTabContainer::onNextBtnHeld, this, _2)); - - mNextArrowBtn = LLUICtrlFactory::create(p); - - if( getTabPosition() == TOP ) - { - mNextArrowBtn->setFollowsTop(); - mPrevArrowBtn->setFollowsTop(); - mJumpPrevArrowBtn->setFollowsTop(); - mJumpNextArrowBtn->setFollowsTop(); - } - else - { - mNextArrowBtn->setFollowsBottom(); - mPrevArrowBtn->setFollowsBottom(); - mJumpPrevArrowBtn->setFollowsBottom(); - mJumpNextArrowBtn->setFollowsBottom(); - } - } - - mPrevArrowBtn->setTabStop(false); - addChild(mPrevArrowBtn); - - mNextArrowBtn->setTabStop(false); - addChild(mNextArrowBtn); - - if (mJumpPrevArrowBtn) - { - mJumpPrevArrowBtn->setTabStop(false); - addChild(mJumpPrevArrowBtn); - } - - if (mJumpNextArrowBtn) - { - mJumpNextArrowBtn->setTabStop(false); - addChild(mJumpNextArrowBtn); - } - - // set default tab group to be panel contents - setDefaultTabGroup(1); -} - -//this is a work around for the current LLPanel::initFromParams hack -//so that it doesn't overwrite the default tab group. -//will be removed when LLPanel is fixed soon. -void LLTabContainer::initFromParams(const LLPanel::Params& p) -{ - LLPanel::initFromParams(p); - - setDefaultTabGroup(1); -} - -LLTabTuple* LLTabContainer::getTabByPanel(LLPanel* child) -{ - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - if( tuple->mTabPanel == child ) - { - return tuple; - } - } - return NULL; -} - -void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point) -{ - switch(insertion_point) - { - case START: - // insert the new tab in the front of the list - mTabList.insert(mTabList.begin() + mLockedTabCount, tuple); - break; - case LEFT_OF_CURRENT: - // insert the new tab before the current tab (but not before mLockedTabCount) - { - tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx); - mTabList.insert(current_iter, tuple); - } - break; - - case RIGHT_OF_CURRENT: - // insert the new tab after the current tab (but not before mLockedTabCount) - { - tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1); - mTabList.insert(current_iter, tuple); - } - break; - case END: - default: - mTabList.push_back( tuple ); - } -} - - - -void LLTabContainer::updateMaxScrollPos() -{ - static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); - bool no_scroll = true; - if (mIsVertical) - { - S32 tab_total_height = (BTN_HEIGHT + tabcntrv_pad) * getTabCount(); - S32 available_height = getRect().getHeight() - getTopBorderHeight(); - if( tab_total_height > available_height ) - { - static LLUICachedControl tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0); - S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad) - mNextArrowBtn->getRect().mBottom; - S32 additional_needed = tab_total_height - available_height_with_arrows; - setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT + tabcntrv_pad) ) ); - no_scroll = false; - } - } - else - { - static LLUICachedControl tabcntr_tab_h_pad ("UITabCntrTabHPad", 0); - static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); - static LLUICachedControl tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0); - S32 tab_space = 0; - S32 available_space = 0; - tab_space = mTotalTabWidth; - available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_tab_h_pad); - - if( tab_space > available_space ) - { - S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); - // subtract off reserved portion on left - available_width_with_arrows -= tabcntr_tab_partial_width; - - S32 running_tab_width = 0; - setMaxScrollPos(getTabCount()); - for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it) - { - running_tab_width += (*tab_it)->mButton->getRect().getWidth(); - if (running_tab_width > available_width_with_arrows) - { - break; - } - setMaxScrollPos(getMaxScrollPos()-1); - } - // in case last tab doesn't actually fit on screen, make it the last scrolling position - setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1)); - no_scroll = false; - } - } - if (no_scroll) - { - setMaxScrollPos(0); - setScrollPos(0); - } - if (getScrollPos() > getMaxScrollPos()) - { - setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead? - } -} - -void LLTabContainer::commitHoveredButton(S32 x, S32 y) -{ - if (!getTabsHidden() && hasMouseCapture()) - { - for (tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLButton* button = (*iter)->mButton; - LLPanel* panel = (*iter)->mTabPanel; - if (button->getEnabled() && button->getVisible() && !panel->getVisible()) - { - S32 local_x = x - button->getRect().mLeft; - S32 local_y = y - button->getRect().mBottom; - if (button->pointInView(local_x, local_y)) - { - button->onCommit(); - break; - } - } - } - } -} - -S32 LLTabContainer::getTotalTabWidth() const -{ - return mTotalTabWidth; -} - -void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible ) -{ - for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr ) - { - LLTabTuple const *pTT = *itr; - if( pTT->mTabPanel == aPanel ) - { - pTT->mVisible = aVisible; - break; - } - } - - bool foundTab( false ); - for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr ) - { - LLTabTuple const *pTT = *itr; - if( pTT->mVisible ) - { - this->selectTab( itr - mTabList.begin() ); - foundTab = true; - break; - } - } - - if( foundTab ) - this->setVisible( true ); - else - this->setVisible( false ); - - updateMaxScrollPos(); -} +/** + * @file lltabcontainer.cpp + * @brief LLTabContainer class + * + * $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 "linden_common.h" + +#include "lltabcontainer.h" +#include "llviewereventrecorder.h" +#include "llfocusmgr.h" +#include "lllocalcliprect.h" +#include "llrect.h" +#include "llresizehandle.h" +#include "lltextbox.h" +#include "llcriticaldamp.h" +#include "lluictrlfactory.h" +#include "llrender.h" +#include "llfloater.h" +#include "lltrans.h" +#include "lluiusage.h" + +//---------------------------------------------------------------------------- + +// Implementation Notes: +// - Each tab points to a LLPanel (see LLTabTuple below) +// - When a tab is selected, the validation callback +// (LLUICtrl::mValidateSignal) is called +// - If the validation callback returns true (or none is provided), +// the tab is changed and the commit callback +// (LLUICtrl::mCommitSignal) is called +// - Callbacks pass the LLTabContainer as the control, +// and the NAME of the selected PANEL as the LLSD data + +//---------------------------------------------------------------------------- + +const F32 SCROLL_STEP_TIME = 0.4f; +const F32 SCROLL_DELAY_TIME = 0.5f; + +void LLTabContainer::TabPositions::declareValues() +{ + declare("top", LLTabContainer::TOP); + declare("bottom", LLTabContainer::BOTTOM); + declare("left", LLTabContainer::LEFT); +} + +//---------------------------------------------------------------------------- + +// Structure used to map tab buttons to and from tab panels +class LLTabTuple +{ +public: + LLTabTuple( LLTabContainer* c, LLPanel* p, LLButton* b, LLTextBox* placeholder = NULL) + : + mTabContainer(c), + mTabPanel(p), + mButton(b), + mOldState(false), + mPlaceholderText(placeholder), + mPadding(0), + mVisible(true) + {} + + LLTabContainer* mTabContainer; + LLPanel* mTabPanel; + LLButton* mButton; + bool mOldState; + LLTextBox* mPlaceholderText; + S32 mPadding; + + mutable bool mVisible; +}; + +//---------------------------------------------------------------------------- + +//============================================================================ +/* + * @file lltabcontainer.cpp + * @brief class implements LLButton with LLIconCtrl on it + */ +class LLCustomButtonIconCtrl : public LLButton +{ +public: + struct Params + : public LLInitParam::Block + { + // LEFT, RIGHT, TOP, BOTTOM paddings of LLIconCtrl in this class has same value + Optional icon_ctrl_pad; + + Params() + : icon_ctrl_pad("icon_ctrl_pad", 1) + {} + }; + +protected: + friend class LLUICtrlFactory; + + LLCustomButtonIconCtrl(const Params& p) + : LLButton(p), + mIcon(NULL), + mIconAlignment(LLFontGL::HCENTER), + mIconCtrlPad(p.icon_ctrl_pad) + {} + +public: + + void updateLayout() + { + LLRect button_rect = getRect(); + LLRect icon_rect = mIcon->getRect(); + + S32 icon_size = button_rect.getHeight() - 2*mIconCtrlPad; + + switch(mIconAlignment) + { + case LLFontGL::LEFT: + icon_rect.setLeftTopAndSize(button_rect.mLeft + mIconCtrlPad, button_rect.mTop - mIconCtrlPad, + icon_size, icon_size); + setLeftHPad(icon_size + mIconCtrlPad * 2); + break; + case LLFontGL::HCENTER: + icon_rect.setLeftTopAndSize(button_rect.mRight - (button_rect.getWidth() + mIconCtrlPad - icon_size)/2, button_rect.mTop - mIconCtrlPad, + icon_size, icon_size); + setRightHPad(icon_size + mIconCtrlPad * 2); + break; + case LLFontGL::RIGHT: + icon_rect.setLeftTopAndSize(button_rect.mRight - mIconCtrlPad - icon_size, button_rect.mTop - mIconCtrlPad, + icon_size, icon_size); + setRightHPad(icon_size + mIconCtrlPad * 2); + break; + default: + break; + } + mIcon->setRect(icon_rect); + } + + void setIcon(LLIconCtrl* icon, LLFontGL::HAlign alignment = LLFontGL::LEFT) + { + if(icon) + { + if(mIcon) + { + removeChild(mIcon); + mIcon->die(); + } + mIcon = icon; + mIconAlignment = alignment; + + addChild(mIcon); + updateLayout(); + } + } + + LLIconCtrl* getIconCtrl() const + { + return mIcon; + } + +private: + LLIconCtrl* mIcon; + LLFontGL::HAlign mIconAlignment; + S32 mIconCtrlPad; +}; +//============================================================================ + +struct LLPlaceHolderPanel : public LLPanel +{ + // create dummy param block to register with "placeholder" nane + struct Params : public LLPanel::Params{}; + LLPlaceHolderPanel(const Params& p) : LLPanel(p) + {} +}; +static LLDefaultChildRegistry::Register r1("placeholder"); +static LLDefaultChildRegistry::Register r2("tab_container"); + +LLTabContainer::TabParams::TabParams() +: tab_top_image_unselected("tab_top_image_unselected"), + tab_top_image_selected("tab_top_image_selected"), + tab_top_image_flash("tab_top_image_flash"), + tab_bottom_image_unselected("tab_bottom_image_unselected"), + tab_bottom_image_selected("tab_bottom_image_selected"), + tab_bottom_image_flash("tab_bottom_image_flash"), + tab_left_image_unselected("tab_left_image_unselected"), + tab_left_image_selected("tab_left_image_selected"), + tab_left_image_flash("tab_left_image_flash") +{} + +LLTabContainer::Params::Params() +: tab_width("tab_width"), + tab_min_width("tab_min_width"), + tab_max_width("tab_max_width"), + tab_height("tab_height"), + label_pad_bottom("label_pad_bottom"), + label_pad_left("label_pad_left"), + tab_position("tab_position"), + hide_tabs("hide_tabs", false), + hide_scroll_arrows("hide_scroll_arrows", false), + tab_padding_right("tab_padding_right"), + first_tab("first_tab"), + middle_tab("middle_tab"), + last_tab("last_tab"), + use_custom_icon_ctrl("use_custom_icon_ctrl", false), + open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false), + enable_tabs_flashing("enable_tabs_flashing", false), + tabs_flashing_color("tabs_flashing_color"), + tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0), + use_ellipses("use_ellipses"), + font_halign("halign"), + use_tab_offset("use_tab_offset", false) +{} + +LLTabContainer::LLTabContainer(const LLTabContainer::Params& p) +: LLPanel(p), + mCurrentTabIdx(-1), + mTabsHidden(p.hide_tabs), + mScrolled(false), + mScrollPos(0), + mScrollPosPixels(0), + mMaxScrollPos(0), + mTitleBox(NULL), + mTopBorderHeight(LLPANEL_BORDER_WIDTH), + mLockedTabCount(0), + mMinTabWidth(0), + mMaxTabWidth(p.tab_max_width), + mTabHeight(p.tab_height), + mLabelPadBottom(p.label_pad_bottom), + mLabelPadLeft(p.label_pad_left), + mPrevArrowBtn(NULL), + mNextArrowBtn(NULL), + mIsVertical( p.tab_position == LEFT ), + mHideScrollArrows(p.hide_scroll_arrows), + // Horizontal Specific + mJumpPrevArrowBtn(NULL), + mJumpNextArrowBtn(NULL), + mRightTabBtnOffset(p.tab_padding_right), + mTotalTabWidth(0), + mTabPosition(p.tab_position), + mFontHalign(p.font_halign), + mFont(p.font), + mFirstTabParams(p.first_tab), + mMiddleTabParams(p.middle_tab), + mLastTabParams(p.last_tab), + mCustomIconCtrlUsed(p.use_custom_icon_ctrl), + mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop), + mTabIconCtrlPad(p.tab_icon_ctrl_pad), + mEnableTabsFlashing(p.enable_tabs_flashing), + mTabsFlashingColor(p.tabs_flashing_color), + mUseTabEllipses(p.use_ellipses), + mUseTabOffset(p.use_tab_offset) +{ + static LLUICachedControl tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0); + + mDragAndDropDelayTimer.stop(); + + if (p.tab_width.isProvided()) + { + mMinTabWidth = p.tab_width; + } + else if (!mIsVertical) + { + mMinTabWidth = p.tab_min_width; + } + else + { + // *HACK: support default min width for legacy vertical + // tab containers + mMinTabWidth = tabcntr_vert_tab_min_width; + } + + if (p.tabs_flashing_color.isProvided()) + { + mEnableTabsFlashing = true; + } + + initButtons( ); +} + +LLTabContainer::~LLTabContainer() +{ + std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); + mTabList.clear(); +} + +//virtual +void LLTabContainer::setValue(const LLSD& value) +{ + selectTab((S32) value.asInteger()); +} + +//virtual +void LLTabContainer::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLPanel::reshape( width, height, called_from_parent ); + updateMaxScrollPos(); +} + +//virtual +LLView* LLTabContainer::getChildView(const std::string& name, bool recurse) const +{ + tuple_list_t::const_iterator itor; + for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) + { + LLPanel *panel = (*itor)->mTabPanel; + if (panel->getName() == name) + { + return panel; + } + } + + if (recurse) + { + for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) + { + LLPanel *panel = (*itor)->mTabPanel; + LLView *child = panel->getChildView(name, recurse); + if (child) + { + return child; + } + } + } + return LLView::getChildView(name, recurse); +} + +//virtual +LLView* LLTabContainer::findChildView(const std::string& name, bool recurse) const +{ + tuple_list_t::const_iterator itor; + for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) + { + LLPanel *panel = (*itor)->mTabPanel; + if (panel->getName() == name) + { + return panel; + } + } + + if (recurse) + { + for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) + { + LLPanel *panel = (*itor)->mTabPanel; + LLView *child = panel->findChildView(name, recurse); + if (child) + { + return child; + } + } + } + return LLView::findChildView(name, recurse); +} + +bool LLTabContainer::addChild(LLView* view, S32 tab_group) +{ + LLPanel* panelp = dynamic_cast(view); + + if (panelp) + { + addTabPanel(TabPanelParams().panel(panelp).label(panelp->getLabel()).is_placeholder(dynamic_cast(view) != NULL)); + return true; + } + else + { + return LLUICtrl::addChild(view, tab_group); + } +} + +bool LLTabContainer::postBuild() +{ + selectFirstTab(); + + return true; +} + +// virtual +void LLTabContainer::draw() +{ + static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); + static LLUICachedControl tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0); + static LLUICachedControl tabcntr_tab_h_pad ("UITabCntrTabHPad", 0); + static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); + static LLUICachedControl tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0); + S32 target_pixel_scroll = 0; + S32 cur_scroll_pos = getScrollPos(); + if (cur_scroll_pos > 0) + { + if (mIsVertical) + { + target_pixel_scroll = cur_scroll_pos * (BTN_HEIGHT + tabcntrv_pad); + } + else + { + S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + if (cur_scroll_pos == 0) + { + break; + } + + if( (*iter)->mVisible ) + target_pixel_scroll += (*iter)->mButton->getRect().getWidth(); + + cur_scroll_pos--; + } + + // Show part of the tab to the left of what is fully visible + target_pixel_scroll -= tabcntr_tab_partial_width; + // clamp so that rightmost tab never leaves right side of screen + target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll); + } + } + + setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLSmoothInterpolation::getInterpolant(0.08f))); + + bool has_scroll_arrows = !mHideScrollArrows && !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0)); + if (!mIsVertical) + { + mJumpPrevArrowBtn->setVisible( has_scroll_arrows ); + mJumpNextArrowBtn->setVisible( has_scroll_arrows ); + } + mPrevArrowBtn->setVisible( has_scroll_arrows ); + mNextArrowBtn->setVisible( has_scroll_arrows ); + + S32 left = 0, top = 0; + if (mIsVertical) + { + top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? tabcntrv_arrow_btn_size : 0); + top += getScrollPosPixels(); + } + else + { + // Set the leftmost position of the tab buttons. + left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (tabcntr_arrow_btn_size * 2) : tabcntr_tab_h_pad); + left -= getScrollPosPixels(); + } + + // Hide all the buttons + if (getTabsHidden()) + { + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + tuple->mButton->setVisible( false ); + } + } + + { + LLRect clip_rect = getLocalRect(); + clip_rect.mLeft+=(LLPANEL_BORDER_WIDTH + 2); + clip_rect.mRight-=(LLPANEL_BORDER_WIDTH + 2); + LLLocalClipRect clip(clip_rect); + LLPanel::draw(); + } + + // if tabs are hidden, don't draw them and leave them in the invisible state + if (!getTabsHidden()) + { + // Show all the buttons + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + tuple->mButton->setVisible( true ); + } + + S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos(); + S32 idx = 0; + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + + if( !tuple->mVisible ) + { + tuple->mButton->setVisible( false ); + continue; + } + + tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0, + top ? top - tuple->mButton->getRect().mTop : 0 ); + if (top) top -= BTN_HEIGHT + tabcntrv_pad; + if (left) left += tuple->mButton->getRect().getWidth(); + + if (!mIsVertical) + { + if( idx < getScrollPos() ) + { + if( tuple->mButton->getFlashing() ) + { + mPrevArrowBtn->setFlashing( true ); + } + } + else if( max_scroll_visible < idx ) + { + if( tuple->mButton->getFlashing() ) + { + mNextArrowBtn->setFlashing( true ); + } + } + } + + idx++; + } + + + if( mIsVertical && has_scroll_arrows ) + { + // Redraw the arrows so that they appears on top. + gGL.pushUIMatrix(); + gGL.translateUI((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f); + mPrevArrowBtn->draw(); + gGL.popUIMatrix(); + + gGL.pushUIMatrix(); + gGL.translateUI((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f); + mNextArrowBtn->draw(); + gGL.popUIMatrix(); + } + } + + mPrevArrowBtn->setFlashing(false); + mNextArrowBtn->setFlashing(false); +} + + +// virtual +bool LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); + bool handled = false; + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); + + if (has_scroll_arrows) + { + if (mJumpPrevArrowBtn&& mJumpPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + handled = mJumpPrevArrowBtn->handleMouseDown(local_x, local_y, mask); + } + else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + handled = mJumpNextArrowBtn->handleMouseDown(local_x, local_y, mask); + } + else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + handled = mPrevArrowBtn->handleMouseDown(local_x, local_y, mask); + } + else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask); + } + } + if (!handled) + { + handled = LLPanel::handleMouseDown( x, y, mask ); + } + + S32 tab_count = getTabCount(); + if (tab_count > 0 && !getTabsHidden()) + { + LLTabTuple* firsttuple = getTab(0); + LLRect tab_rect; + if (mIsVertical) + { + tab_rect = LLRect(firsttuple->mButton->getRect().mLeft, + has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop, + firsttuple->mButton->getRect().mRight, + has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom ); + } + else + { + tab_rect = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft, + firsttuple->mButton->getRect().mTop, + has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight, + firsttuple->mButton->getRect().mBottom ); + } + if( tab_rect.pointInRect( x, y ) ) + { + S32 index = getCurrentPanelIndex(); + index = llclamp(index, 0, tab_count-1); + LLButton* tab_button = getTab(index)->mButton; + gFocusMgr.setMouseCapture(this); + tab_button->setFocus(true); + mMouseDownTimer.start(); + } + } + if (handled) { + // Note: May need to also capture local coords right here ? + LLViewerEventRecorder::instance().update_xui(getPathname( )); + } + + return handled; +} + +// virtual +bool LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) +{ + bool handled = false; + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); + + if (has_scroll_arrows) + { + if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + handled = mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + handled = mJumpNextArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + handled = mPrevArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + handled = mNextArrowBtn->handleHover(local_x, local_y, mask); + } + } + if (!handled) + { + handled = LLPanel::handleHover(x, y, mask); + } + + F32 drag_delay = 0.25f; // filter out clicks from dragging + if (mMouseDownTimer.getElapsedTimeF32() > drag_delay) + { + commitHoveredButton(x, y); + } + return handled; +} + +// virtual +bool LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + bool handled = false; + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden(); + + S32 local_x = x - getRect().mLeft; + S32 local_y = y - getRect().mBottom; + + if (has_scroll_arrows) + { + if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) + { + local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask); + } + else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + local_x = x - mJumpNextArrowBtn->getRect().mLeft; + local_y = y - mJumpNextArrowBtn->getRect().mBottom; + handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask); + } + else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) + { + local_x = x - mPrevArrowBtn->getRect().mLeft; + local_y = y - mPrevArrowBtn->getRect().mBottom; + handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask); + } + else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) + { + local_x = x - mNextArrowBtn->getRect().mLeft; + local_y = y - mNextArrowBtn->getRect().mBottom; + handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask); + } + } + if (!handled) + { + handled = LLPanel::handleMouseUp( x, y, mask ); + } + + commitHoveredButton(x, y); + mMouseDownTimer.stop(); + LLPanel* cur_panel = getCurrentPanel(); + if (hasMouseCapture()) + { + if (cur_panel) + { + if (!cur_panel->focusFirstItem(false)) + { + // if nothing in the panel gets focus, make sure the new tab does + // otherwise the last tab might keep focus + getTab(getCurrentPanelIndex())->mButton->setFocus(true); + } + } + gFocusMgr.setMouseCapture(NULL); + } + if (handled) { + // Note: may need to capture local coords here + LLViewerEventRecorder::instance().update_xui(getPathname( )); + } + return handled; +} + +// virtual +bool LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask) +{ + static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); + bool handled = LLPanel::handleToolTip( x, y, mask); + if (!handled && getTabCount() > 0 && !getTabsHidden()) + { + LLTabTuple* firsttuple = getTab(0); + + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); + LLRect clip; + if (mIsVertical) + { + clip = LLRect(firsttuple->mButton->getRect().mLeft, + has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop, + firsttuple->mButton->getRect().mRight, + has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom ); + } + else + { + clip = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft, + firsttuple->mButton->getRect().mTop, + has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight, + firsttuple->mButton->getRect().mBottom ); + } + + if( clip.pointInRect( x, y ) ) + { + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLButton* tab_button = (*iter)->mButton; + if (!tab_button->getVisible()) continue; + S32 local_x = x - tab_button->getRect().mLeft; + S32 local_y = y - tab_button->getRect().mBottom; + handled = tab_button->handleToolTip(local_x, local_y, mask); + if( handled ) + { + break; + } + } + } + } + return handled; +} + +// virtual +bool LLTabContainer::handleKeyHere(KEY key, MASK mask) +{ + bool handled = false; + if (key == KEY_LEFT && mask == MASK_ALT) + { + selectPrevTab(); + handled = true; + } + else if (key == KEY_RIGHT && mask == MASK_ALT) + { + selectNextTab(); + handled = true; + } + + if (handled) + { + if (getCurrentPanel()) + { + getCurrentPanel()->setFocus(true); + } + } + + if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel())) + { + // if child has focus, but not the current panel, focus is on a button + if (mIsVertical) + { + switch(key) + { + case KEY_UP: + selectPrevTab(); + handled = true; + break; + case KEY_DOWN: + selectNextTab(); + handled = true; + break; + case KEY_LEFT: + handled = true; + break; + case KEY_RIGHT: + if (getTabPosition() == LEFT && getCurrentPanel()) + { + getCurrentPanel()->setFocus(true); + } + handled = true; + break; + default: + break; + } + } + else + { + switch(key) + { + case KEY_UP: + if (getTabPosition() == BOTTOM && getCurrentPanel()) + { + getCurrentPanel()->setFocus(true); + } + handled = true; + break; + case KEY_DOWN: + if (getTabPosition() == TOP && getCurrentPanel()) + { + getCurrentPanel()->setFocus(true); + } + handled = true; + break; + case KEY_LEFT: + selectPrevTab(); + handled = true; + break; + case KEY_RIGHT: + selectNextTab(); + handled = true; + break; + default: + break; + } + } + } + return handled; +} + +// virtual +bool LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string &tooltip) +{ + bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0); + + if(mOpenTabsOnDragAndDrop && !getTabsHidden()) + { + // In that case, we'll open the hovered tab while dragging and dropping items. + // This allows for drilling through tabs. + if (mDragAndDropDelayTimer.getStarted()) + { + if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME) + { + if (has_scroll_arrows) + { + if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); + } + if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + mJumpNextArrowBtn->handleHover(local_x, local_y, mask); + } + if (mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + mPrevArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + mNextArrowBtn->handleHover(local_x, local_y, mask); + } + } + + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + tuple->mButton->setVisible( true ); + S32 local_x = x - tuple->mButton->getRect().mLeft; + S32 local_y = y - tuple->mButton->getRect().mBottom; + if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible()) + { + tuple->mButton->onCommit(); + } + } + // Stop the timer whether successful or not. Don't let it run forever. + mDragAndDropDelayTimer.stop(); + } + } + else + { + // Start a timer so we don't open tabs as soon as we hover on them + mDragAndDropDelayTimer.start(); + } + } + + return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip); +} + +void LLTabContainer::addTabPanel(LLPanel* panelp) +{ + addTabPanel(TabPanelParams().panel(panelp)); +} + +// function to update images +void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos) +{ + if (tuple && tuple->mButton) + { + if (pos == LLTabContainer::TOP) + { + tuple->mButton->setImageUnselected(static_cast(params.tab_top_image_unselected)); + tuple->mButton->setImageSelected(static_cast(params.tab_top_image_selected)); + tuple->mButton->setImageFlash(static_cast(params.tab_top_image_flash)); + } + else if (pos == LLTabContainer::BOTTOM) + { + tuple->mButton->setImageUnselected(static_cast(params.tab_bottom_image_unselected)); + tuple->mButton->setImageSelected(static_cast(params.tab_bottom_image_selected)); + tuple->mButton->setImageFlash(static_cast(params.tab_bottom_image_flash)); + } + else if (pos == LLTabContainer::LEFT) + { + tuple->mButton->setImageUnselected(static_cast(params.tab_left_image_unselected)); + tuple->mButton->setImageSelected(static_cast(params.tab_left_image_selected)); + tuple->mButton->setImageFlash(static_cast(params.tab_left_image_flash)); + } + } +} + +void LLTabContainer::addTabPanel(const TabPanelParams& panel) +{ + LLPanel* child = panel.panel(); + + llassert(child); + if (!child) return; + + const std::string& label = panel.label.isProvided() + ? panel.label() + : panel.panel()->getLabel(); + bool select = panel.select_tab(); + S32 indent = panel.indent(); + bool placeholder = panel.is_placeholder; + eInsertionPoint insertion_point = panel.insert_at(); + + static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); + static LLUICachedControl tabcntr_button_panel_overlap ("UITabCntrButtonPanelOverlap", 0); + static LLUICachedControl tab_padding ("UITabPadding", 0); + if (child->getParent() == this) + { + // already a child of mine + return; + } + + // Store the original label for possible xml export. + child->setLabel(label); + std::string trimmed_label = label; + LLStringUtil::trim(trimmed_label); + + S32 button_width = mMinTabWidth; + if (!mIsVertical) + { + button_width = llclamp(mFont->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth); + } + + // Tab panel + S32 tab_panel_top; + S32 tab_panel_bottom; + if (!getTabsHidden()) + { + if( getTabPosition() == LLTabContainer::TOP ) + { + S32 tab_height = mIsVertical ? BTN_HEIGHT : mTabHeight; + tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - tabcntr_button_panel_overlap); + tab_panel_bottom = LLPANEL_BORDER_WIDTH; + } + else + { + tab_panel_top = getRect().getHeight() - getTopBorderHeight(); + tab_panel_bottom = (mTabHeight - tabcntr_button_panel_overlap); // Run to the edge, covering up the border + } + } + else + { + // Skip tab button space if tabs are invisible (EXT-576) + tab_panel_top = getRect().getHeight(); + tab_panel_bottom = LLPANEL_BORDER_WIDTH; + } + + LLRect tab_panel_rect; + if (!getTabsHidden() && mIsVertical) + { + tab_panel_rect = LLRect(mMinTabWidth + mRightTabBtnOffset + (LLPANEL_BORDER_WIDTH * 2) + tabcntrv_pad, + getRect().getHeight() - LLPANEL_BORDER_WIDTH, + getRect().getWidth() - LLPANEL_BORDER_WIDTH, + LLPANEL_BORDER_WIDTH); + } + else + { + S32 left_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 3 : LLPANEL_BORDER_WIDTH; + S32 right_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 2 : LLPANEL_BORDER_WIDTH; + tab_panel_rect = LLRect(left_offset, tab_panel_top, getRect().getWidth() - right_offset, tab_panel_bottom); + } + child->setFollowsAll(); + child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom); + child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), true ); + // add this child later + + child->setVisible( false ); // Will be made visible when selected + + mTotalTabWidth += button_width; + + // Tab button + LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw(). + LLUIImage* tab_img = NULL; + LLUIImage* tab_selected_img = NULL; + S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel + + if (mIsVertical) + { + btn_rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor + (getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * getTabCount()), + mMinTabWidth, + BTN_HEIGHT); + } + else if( getTabPosition() == LLTabContainer::TOP ) + { + btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, mTabHeight); + tab_img = mMiddleTabParams.tab_top_image_unselected; + tab_selected_img = mMiddleTabParams.tab_top_image_selected; + } + else + { + btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, mTabHeight); + tab_img = mMiddleTabParams.tab_bottom_image_unselected; + tab_selected_img = mMiddleTabParams.tab_bottom_image_selected; + } + + LLTextBox* textbox = NULL; + LLButton* btn = NULL; + LLCustomButtonIconCtrl::Params custom_btn_params; + { + custom_btn_params.icon_ctrl_pad(mTabIconCtrlPad); + } + LLButton::Params normal_btn_params; + + if (placeholder) + { + btn_rect.translate(0, -6); // *TODO: make configurable + LLTextBox::Params params; + params.name(trimmed_label); + params.rect(btn_rect); + params.initial_value(trimmed_label); + params.font(mFont); + textbox = LLUICtrlFactory::create (params); + + LLButton::Params p; + p.name("placeholder"); + btn = LLUICtrlFactory::create(p); + } + else + { + LLButton::Params& p = (mCustomIconCtrlUsed ? custom_btn_params : normal_btn_params); + + p.rect(btn_rect); + p.font(mFont); + p.font_halign = mFontHalign; + p.label(trimmed_label); + p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child)); + if (indent) + { + p.pad_left(indent); + } + p.pad_bottom( mLabelPadBottom ); + p.scale_image(true); + p.tab_stop(false); + p.label_shadow(false); + p.follows.flags = FOLLOWS_LEFT; + + if (mIsVertical) + { + p.name("vtab_"+std::string(child->getName())); + p.image_unselected(mMiddleTabParams.tab_left_image_unselected); + p.image_selected(mMiddleTabParams.tab_left_image_selected); + p.follows.flags = p.follows.flags() | FOLLOWS_TOP; + } + else + { + p.name("htab_"+std::string(child->getName())); + p.visible(false); + p.image_unselected(tab_img); + p.image_selected(tab_selected_img); + p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM); + // Try to squeeze in a bit more text + p.pad_left( mLabelPadLeft ); + p.pad_right(2); + } + + // inits flash timer + p.button_flash_enable = mEnableTabsFlashing; + p.flash_color = mTabsFlashingColor; + + // *TODO : It seems wrong not to use p in both cases considering the way p is initialized + if (mCustomIconCtrlUsed) + { + btn = LLUICtrlFactory::create(custom_btn_params); + } + else + { + btn = LLUICtrlFactory::create(p); + } + } + + LLTabTuple* tuple = new LLTabTuple( this, child, btn, textbox ); + insertTuple( tuple, insertion_point ); + + // if new tab was added as a first or last tab, update button image + // and update button image of any tab it may have affected + if (tuple == mTabList.front()) + { + update_images(tuple, mFirstTabParams, getTabPosition()); + + if (mTabList.size() == 2) + { + update_images(mTabList[1], mLastTabParams, getTabPosition()); + } + else if (mTabList.size() > 2) + { + update_images(mTabList[1], mMiddleTabParams, getTabPosition()); + } + } + else if (tuple == mTabList.back()) + { + update_images(tuple, mLastTabParams, getTabPosition()); + + if (mTabList.size() > 2) + { + update_images(mTabList[mTabList.size()-2], mMiddleTabParams, getTabPosition()); + } + } + + //Don't add button and textbox if tab buttons are invisible(EXT - 576) + if (!getTabsHidden()) + { + if (textbox) + { + addChild( textbox, 0 ); + } + if (btn) + { + addChild( btn, 0 ); + } + } + else + { + if (textbox) + { + LLUICtrl::addChild(textbox, 0); + } + if (btn) + { + LLUICtrl::addChild(btn, 0); + } + } + + if (child) + { + LLUICtrl::addChild(child, 1); + } + + sendChildToFront(mPrevArrowBtn); + sendChildToFront(mNextArrowBtn); + sendChildToFront(mJumpPrevArrowBtn); + sendChildToFront(mJumpNextArrowBtn); + + updateMaxScrollPos(); + + if( select ) + { + selectLastTab(); + mScrollPos = mMaxScrollPos; + } + +} + +void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label) +{ + addTabPanel(TabPanelParams().panel(child).label(label).is_placeholder(true)); +} + +void LLTabContainer::removeTabPanel(LLPanel* child) +{ + static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); + if (mIsVertical) + { + // Fix-up button sizes + S32 tab_count = 0; + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + LLRect rect; + rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor + (getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * (tab_count)), + mMinTabWidth, + BTN_HEIGHT); + if (tuple->mPlaceholderText) + { + tuple->mPlaceholderText->setRect(rect); + } + else + { + tuple->mButton->setRect(rect); + } + tab_count++; + } + } + else + { + // Adjust the total tab width. + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + if( tuple->mTabPanel == child ) + { + mTotalTabWidth -= tuple->mButton->getRect().getWidth(); + break; + } + } + } + + bool has_focus = gFocusMgr.childHasKeyboardFocus(this); + + // If the tab being deleted is the selected one, select a different tab. + for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + if( tuple->mTabPanel == child ) + { + // update tab button images if removing the first or last tab + if ((tuple == mTabList.front()) && (mTabList.size() > 1)) + { + update_images(mTabList[1], mFirstTabParams, getTabPosition()); + } + else if ((tuple == mTabList.back()) && (mTabList.size() > 2)) + { + update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition()); + } + + if (!getTabsHidden()) + { + // We need to remove tab buttons only if the tabs are not hidden. + removeChild( tuple->mButton ); + } + delete tuple->mButton; + tuple->mButton = NULL; + + removeChild( tuple->mTabPanel ); +// delete tuple->mTabPanel; + tuple->mTabPanel = NULL; + + mTabList.erase( iter ); + delete tuple; + + break; + } + } + + // make sure we don't have more locked tabs than we have tabs + mLockedTabCount = llmin(getTabCount(), mLockedTabCount); + + if (mCurrentTabIdx >= (S32)mTabList.size()) + { + mCurrentTabIdx = mTabList.size()-1; + } + selectTab(mCurrentTabIdx); + if (has_focus) + { + LLPanel* panelp = getPanelByIndex(mCurrentTabIdx); + if (panelp) + { + panelp->setFocus(true); + } + } + + updateMaxScrollPos(); +} + +void LLTabContainer::lockTabs(S32 num_tabs) +{ + // count current tabs or use supplied value and ensure no new tabs get + // inserted between them + mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount(); +} + +void LLTabContainer::unlockTabs() +{ + mLockedTabCount = 0; +} + +void LLTabContainer::enableTabButton(S32 which, bool enable) +{ + if (which >= 0 && which < (S32)mTabList.size()) + { + mTabList[which]->mButton->setEnabled(enable); + } + // Stop the DaD timer as it might run forever + // enableTabButton() is typically called on refresh and draw when anything changed + // in the tab container so it's a good time to reset that. + mDragAndDropDelayTimer.stop(); +} + +void LLTabContainer::deleteAllTabs() +{ + // Remove all the tab buttons and delete them. Also, unlink all the child panels. + for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + + removeChild( tuple->mButton ); + delete tuple->mButton; + tuple->mButton = NULL; + + removeChild( tuple->mTabPanel ); +// delete tuple->mTabPanel; + tuple->mTabPanel = NULL; + } + + // Actually delete the tuples themselves + std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); + mTabList.clear(); + + // And there isn't a current tab any more + mCurrentTabIdx = -1; +} + +LLPanel* LLTabContainer::getCurrentPanel() +{ + if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size()) + { + return mTabList[mCurrentTabIdx]->mTabPanel; + } + return NULL; +} + +S32 LLTabContainer::getCurrentPanelIndex() +{ + return mCurrentTabIdx; +} + +S32 LLTabContainer::getTabCount() +{ + return mTabList.size(); +} + +LLPanel* LLTabContainer::getPanelByIndex(S32 index) +{ + if (index >= 0 && index < (S32)mTabList.size()) + { + return mTabList[index]->mTabPanel; + } + return NULL; +} + +S32 LLTabContainer::getIndexForPanel(LLPanel* panel) +{ + for (S32 index = 0; index < (S32)mTabList.size(); index++) + { + if (mTabList[index]->mTabPanel == panel) + { + return index; + } + } + return -1; +} + +S32 LLTabContainer::getPanelIndexByTitle(const std::string& title) +{ + for (S32 index = 0 ; index < (S32)mTabList.size(); index++) + { + if (title == mTabList[index]->mButton->getLabelSelected()) + { + return index; + } + } + return -1; +} + +LLPanel* LLTabContainer::getPanelByName(const std::string& name) +{ + for (S32 index = 0 ; index < (S32)mTabList.size(); index++) + { + LLPanel *panel = mTabList[index]->mTabPanel; + if (name == panel->getName()) + { + return panel; + } + } + return NULL; +} + +// Change the name of the button for the current tab. +void LLTabContainer::setCurrentTabName(const std::string& name) +{ + // Might not have a tab selected + if (mCurrentTabIdx < 0) return; + + mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name); + mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name); +} + +void LLTabContainer::selectFirstTab() +{ + selectTab( 0 ); +} + + +void LLTabContainer::selectLastTab() +{ + selectTab( mTabList.size()-1 ); +} + +void LLTabContainer::selectNextTab() +{ + if (mTabList.size() == 0) + { + return; + } + + bool tab_has_focus = false; + if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus()) + { + tab_has_focus = true; + } + S32 idx = mCurrentTabIdx+1; + if (idx >= (S32)mTabList.size()) + idx = 0; + while (!selectTab(idx) && idx != mCurrentTabIdx) + { + idx = (idx + 1 ) % (S32)mTabList.size(); + } + + if (tab_has_focus) + { + mTabList[idx]->mButton->setFocus(true); + } +} + +void LLTabContainer::selectPrevTab() +{ + bool tab_has_focus = false; + if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus()) + { + tab_has_focus = true; + } + S32 idx = mCurrentTabIdx-1; + if (idx < 0) + idx = mTabList.size()-1; + while (!selectTab(idx) && idx != mCurrentTabIdx) + { + idx = idx - 1; + if (idx < 0) + idx = mTabList.size()-1; + } + if (tab_has_focus) + { + mTabList[idx]->mButton->setFocus(true); + } +} + +bool LLTabContainer::selectTabPanel(LLPanel* child) +{ + S32 idx = 0; + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + if( tuple->mTabPanel == child ) + { + return selectTab( idx ); + } + idx++; + } + return false; +} + +bool LLTabContainer::selectTab(S32 which) +{ + if (which >= getTabCount() || which < 0) + return false; + + LLTabTuple* selected_tuple = getTab(which); + if (!selected_tuple) + return false; + + LLSD cbdata; + if (selected_tuple->mTabPanel) + cbdata = selected_tuple->mTabPanel->getName(); + + bool result = false; + if (!mValidateSignal || (*mValidateSignal)(this, cbdata)) + { + result = setTab(which); + if (result && mCommitSignal) + { + (*mCommitSignal)(this, cbdata); + } + } + + return result; +} + +// private +bool LLTabContainer::setTab(S32 which) +{ + static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); + LLTabTuple* selected_tuple = getTab(which); + if (!selected_tuple) + { + return false; + } + + bool is_visible = false; + if( selected_tuple->mButton->getEnabled() && selected_tuple->mVisible ) + { + setCurrentPanelIndex(which); + + S32 i = 0; + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + bool is_selected = ( tuple == selected_tuple ); + // Although the selected tab must be complete, we may have hollow LLTabTuple tucked in the list + if (tuple && tuple->mButton) + { + tuple->mButton->setUseEllipses(mUseTabEllipses); + tuple->mButton->setHAlign(mFontHalign); + tuple->mButton->setToggleState( is_selected ); + // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs + tuple->mButton->setTabStop( is_selected ); + } + if (tuple && tuple->mTabPanel) + { + tuple->mTabPanel->setVisible( is_selected ); + //tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here. + } + + if (is_selected) + { + LLUIUsage::instance().logPanel(tuple->mTabPanel->getName()); + + // Make sure selected tab is within scroll region + if (mIsVertical) + { + S32 num_visible = getTabCount() - getMaxScrollPos(); + if( i >= getScrollPos() && i <= getScrollPos() + num_visible) + { + setCurrentPanelIndex(which); + is_visible = true; + } + else + { + is_visible = false; + } + } + else if (!mHideScrollArrows && getMaxScrollPos() > 0) + { + if( i < getScrollPos() ) + { + setScrollPos(i); + } + else + { + S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); + S32 running_tab_width = (tuple && tuple->mButton ? tuple->mButton->getRect().getWidth() : 0); + S32 j = i - 1; + S32 min_scroll_pos = i; + if (running_tab_width < available_width_with_arrows) + { + while (j >= 0) + { + LLTabTuple* other_tuple = getTab(j); + running_tab_width += (other_tuple && other_tuple->mButton ? other_tuple->mButton->getRect().getWidth() : 0); + if (running_tab_width > available_width_with_arrows) + { + break; + } + j--; + } + min_scroll_pos = j + 1; + } + setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i)); + setScrollPos(llmin(getScrollPos(), getMaxScrollPos())); + } + is_visible = true; + } + else + { + is_visible = true; + } + } + i++; + } + } + if (mIsVertical && getCurrentPanelIndex() >= 0) + { + LLTabTuple* tuple = getTab(getCurrentPanelIndex()); + tuple->mTabPanel->setVisible( true ); + tuple->mButton->setToggleState( true ); + } + return is_visible; +} + +bool LLTabContainer::selectTabByName(const std::string& name) +{ + LLPanel* panel = getPanelByName(name); + if (!panel) + { + LL_WARNS() << "LLTabContainer::selectTabByName(" << name << ") failed" << LL_ENDL; + return false; + } + + bool result = selectTabPanel(panel); + return result; +} + +bool LLTabContainer::getTabPanelFlashing(LLPanel *child) +{ + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) + { + return tuple->mButton->getFlashing(); + } + return false; +} + +void LLTabContainer::setTabPanelFlashing(LLPanel* child, bool state ) +{ + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) + { + tuple->mButton->setFlashing( state ); + } +} + +void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color) +{ + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) + { + tuple->mButton->setImageOverlay(image_name, LLFontGL::LEFT, color); + reshapeTuple(tuple); + } +} + +void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color) +{ + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) + { + tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color); + reshapeTuple(tuple); + } +} + +void LLTabContainer::setTabImage(LLPanel* child, LLIconCtrl* icon) +{ + LLTabTuple* tuple = getTabByPanel(child); + LLCustomButtonIconCtrl* button; + bool hasButton = false; + + if(tuple) + { + button = dynamic_cast(tuple->mButton); + if(button) + { + hasButton = true; + button->setIcon(icon); + reshapeTuple(tuple); + } + } + + if (!hasButton && (icon != NULL)) + { + // It was assumed that the tab's button would take ownership of the icon pointer. + // But since the tab did not have a button, kill the icon to prevent the memory + // leak. + icon->die(); + } +} + +void LLTabContainer::reshapeTuple(LLTabTuple* tuple) +{ + static LLUICachedControl tab_padding ("UITabPadding", 0); + + if (!mIsVertical) + { + S32 image_overlay_width = 0; + + if(mCustomIconCtrlUsed) + { + LLCustomButtonIconCtrl* button = dynamic_cast(tuple->mButton); + LLIconCtrl* icon_ctrl = button ? button->getIconCtrl() : NULL; + image_overlay_width = icon_ctrl ? icon_ctrl->getRect().getWidth() : 0; + } + else + { + image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? + tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0; + } + // remove current width from total tab strip width + mTotalTabWidth -= tuple->mButton->getRect().getWidth(); + + tuple->mPadding = image_overlay_width; + + tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), + tuple->mButton->getRect().getHeight()); + // add back in button width to total tab strip width + mTotalTabWidth += tuple->mButton->getRect().getWidth(); + + // tabs have changed size, might need to scroll to see current tab + updateMaxScrollPos(); + } +} + +void LLTabContainer::setTitle(const std::string& title) +{ + if (mTitleBox) + { + mTitleBox->setText( title ); + } +} + +const std::string LLTabContainer::getPanelTitle(S32 index) +{ + if (index >= 0 && index < (S32)mTabList.size()) + { + LLButton* tab_button = mTabList[index]->mButton; + return tab_button->getLabelSelected(); + } + return LLStringUtil::null; +} + +void LLTabContainer::setTopBorderHeight(S32 height) +{ + mTopBorderHeight = height; +} + +S32 LLTabContainer::getTopBorderHeight() const +{ + return mTopBorderHeight; +} + +void LLTabContainer::setRightTabBtnOffset(S32 offset) +{ + mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 ); + mRightTabBtnOffset = offset; + updateMaxScrollPos(); +} + +void LLTabContainer::setPanelTitle(S32 index, const std::string& title) +{ + static LLUICachedControl tab_padding ("UITabPadding", 0); + + if (index >= 0 && index < getTabCount()) + { + LLTabTuple* tuple = getTab(index); + LLButton* tab_button = tuple->mButton; + const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall(); + mTotalTabWidth -= tab_button->getRect().getWidth(); + tab_button->reshape(llclamp(fontp->getWidth(title) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight()); + mTotalTabWidth += tab_button->getRect().getWidth(); + tab_button->setLabelSelected(title); + tab_button->setLabelUnselected(title); + } + updateMaxScrollPos(); +} + + +void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel ) +{ + LLTabTuple* tuple = getTabByPanel(panel); + selectTabPanel( panel ); + + if (tuple) + { + tuple->mTabPanel->setFocus(true); + } +} + +void LLTabContainer::onNextBtn( const LLSD& data ) +{ + if (!mScrolled) + { + scrollNext(); + } + mScrolled = false; + + if(mCurrentTabIdx < mTabList.size()-1) + { + selectNextTab(); + } +} + +void LLTabContainer::onNextBtnHeld( const LLSD& data ) +{ + if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) + { + mScrollTimer.reset(); + scrollNext(); + + if(mCurrentTabIdx < mTabList.size()-1) + { + selectNextTab(); + } + mScrolled = true; + } +} + +void LLTabContainer::onPrevBtn( const LLSD& data ) +{ + if (!mScrolled) + { + scrollPrev(); + } + mScrolled = false; + + if(mCurrentTabIdx > 0) + { + selectPrevTab(); + } +} + +void LLTabContainer::onJumpFirstBtn( const LLSD& data ) +{ + mScrollPos = 0; +} + +void LLTabContainer::onJumpLastBtn( const LLSD& data ) +{ + mScrollPos = mMaxScrollPos; +} + +void LLTabContainer::onPrevBtnHeld( const LLSD& data ) +{ + if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) + { + mScrollTimer.reset(); + scrollPrev(); + + if(mCurrentTabIdx > 0) + { + selectPrevTab(); + } + mScrolled = true; + } +} + +// private + +void LLTabContainer::initButtons() +{ + // Hack: + if (getRect().getHeight() == 0 || mPrevArrowBtn) + { + return; // Don't have a rect yet or already got called + } + + if (mIsVertical) + { + static LLUICachedControl tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0); + // Left and right scroll arrows (for when there are too many tabs to show all at once). + S32 btn_top = getRect().getHeight(); + S32 btn_top_lower = getRect().mBottom+tabcntrv_arrow_btn_size; + + LLRect up_arrow_btn_rect; + up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size ); + + LLRect down_arrow_btn_rect; + down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size ); + + LLButton::Params prev_btn_params; + prev_btn_params.name(std::string("Up Arrow")); + prev_btn_params.rect(up_arrow_btn_rect); + prev_btn_params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT); + prev_btn_params.image_unselected.name("scrollbutton_up_out_blue.tga"); + prev_btn_params.image_selected.name("scrollbutton_up_in_blue.tga"); + prev_btn_params.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2)); + mPrevArrowBtn = LLUICtrlFactory::create(prev_btn_params); + + LLButton::Params next_btn_params; + next_btn_params.name(std::string("Down Arrow")); + next_btn_params.rect(down_arrow_btn_rect); + next_btn_params.follows.flags(FOLLOWS_BOTTOM | FOLLOWS_LEFT); + next_btn_params.image_unselected.name("scrollbutton_down_out_blue.tga"); + next_btn_params.image_selected.name("scrollbutton_down_in_blue.tga"); + next_btn_params.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2)); + mNextArrowBtn = LLUICtrlFactory::create(next_btn_params); + } + else // Horizontal + { + static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); + S32 arrow_fudge = 1; // match new art better + + // Left and right scroll arrows (for when there are too many tabs to show all at once). + S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : tabcntr_arrow_btn_size + 1; + + LLRect left_arrow_btn_rect; + left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+tabcntr_arrow_btn_size, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight ); + + LLRect jump_left_arrow_btn_rect; + jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight ); + + S32 right_pad = tabcntr_arrow_btn_size + LLPANEL_BORDER_WIDTH + 1; + + LLRect right_arrow_btn_rect; + right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - tabcntr_arrow_btn_size, + btn_top + arrow_fudge, + tabcntr_arrow_btn_size, mTabHeight ); + + + LLRect jump_right_arrow_btn_rect; + jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad, + btn_top + arrow_fudge, + tabcntr_arrow_btn_size, mTabHeight ); + + LLButton::Params p; + p.name(std::string("Jump Left Arrow")); + p.image_unselected.name("jump_left_out.tga"); + p.image_selected.name("jump_left_in.tga"); + p.click_callback.function(boost::bind(&LLTabContainer::onJumpFirstBtn, this, _2)); + p.rect(jump_left_arrow_btn_rect); + p.follows.flags(FOLLOWS_LEFT); + + mJumpPrevArrowBtn = LLUICtrlFactory::create(p); + + p = LLButton::Params(); + p.name(std::string("Left Arrow")); + p.rect(left_arrow_btn_rect); + p.follows.flags(FOLLOWS_LEFT); + p.image_unselected.name("scrollbutton_left_out_blue.tga"); + p.image_selected.name("scrollbutton_left_in_blue.tga"); + p.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2)); + p.mouse_held_callback.function(boost::bind(&LLTabContainer::onPrevBtnHeld, this, _2)); + + mPrevArrowBtn = LLUICtrlFactory::create(p); + + p = LLButton::Params(); + p.name(std::string("Jump Right Arrow")); + p.rect(jump_right_arrow_btn_rect); + p.follows.flags(FOLLOWS_RIGHT); + p.image_unselected.name("jump_right_out.tga"); + p.image_selected.name("jump_right_in.tga"); + p.click_callback.function(boost::bind(&LLTabContainer::onJumpLastBtn, this, _2)); + + mJumpNextArrowBtn = LLUICtrlFactory::create(p); + + p = LLButton::Params(); + p.name(std::string("Right Arrow")); + p.rect(right_arrow_btn_rect); + p.follows.flags(FOLLOWS_RIGHT); + p.image_unselected.name("scrollbutton_right_out_blue.tga"); + p.image_selected.name("scrollbutton_right_in_blue.tga"); + p.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2)); + p.mouse_held_callback.function(boost::bind(&LLTabContainer::onNextBtnHeld, this, _2)); + + mNextArrowBtn = LLUICtrlFactory::create(p); + + if( getTabPosition() == TOP ) + { + mNextArrowBtn->setFollowsTop(); + mPrevArrowBtn->setFollowsTop(); + mJumpPrevArrowBtn->setFollowsTop(); + mJumpNextArrowBtn->setFollowsTop(); + } + else + { + mNextArrowBtn->setFollowsBottom(); + mPrevArrowBtn->setFollowsBottom(); + mJumpPrevArrowBtn->setFollowsBottom(); + mJumpNextArrowBtn->setFollowsBottom(); + } + } + + mPrevArrowBtn->setTabStop(false); + addChild(mPrevArrowBtn); + + mNextArrowBtn->setTabStop(false); + addChild(mNextArrowBtn); + + if (mJumpPrevArrowBtn) + { + mJumpPrevArrowBtn->setTabStop(false); + addChild(mJumpPrevArrowBtn); + } + + if (mJumpNextArrowBtn) + { + mJumpNextArrowBtn->setTabStop(false); + addChild(mJumpNextArrowBtn); + } + + // set default tab group to be panel contents + setDefaultTabGroup(1); +} + +//this is a work around for the current LLPanel::initFromParams hack +//so that it doesn't overwrite the default tab group. +//will be removed when LLPanel is fixed soon. +void LLTabContainer::initFromParams(const LLPanel::Params& p) +{ + LLPanel::initFromParams(p); + + setDefaultTabGroup(1); +} + +LLTabTuple* LLTabContainer::getTabByPanel(LLPanel* child) +{ + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + if( tuple->mTabPanel == child ) + { + return tuple; + } + } + return NULL; +} + +void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point) +{ + switch(insertion_point) + { + case START: + // insert the new tab in the front of the list + mTabList.insert(mTabList.begin() + mLockedTabCount, tuple); + break; + case LEFT_OF_CURRENT: + // insert the new tab before the current tab (but not before mLockedTabCount) + { + tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx); + mTabList.insert(current_iter, tuple); + } + break; + + case RIGHT_OF_CURRENT: + // insert the new tab after the current tab (but not before mLockedTabCount) + { + tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1); + mTabList.insert(current_iter, tuple); + } + break; + case END: + default: + mTabList.push_back( tuple ); + } +} + + + +void LLTabContainer::updateMaxScrollPos() +{ + static LLUICachedControl tabcntrv_pad ("UITabCntrvPad", 0); + bool no_scroll = true; + if (mIsVertical) + { + S32 tab_total_height = (BTN_HEIGHT + tabcntrv_pad) * getTabCount(); + S32 available_height = getRect().getHeight() - getTopBorderHeight(); + if( tab_total_height > available_height ) + { + static LLUICachedControl tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0); + S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad) - mNextArrowBtn->getRect().mBottom; + S32 additional_needed = tab_total_height - available_height_with_arrows; + setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT + tabcntrv_pad) ) ); + no_scroll = false; + } + } + else + { + static LLUICachedControl tabcntr_tab_h_pad ("UITabCntrTabHPad", 0); + static LLUICachedControl tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0); + static LLUICachedControl tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0); + S32 tab_space = 0; + S32 available_space = 0; + tab_space = mTotalTabWidth; + available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_tab_h_pad); + + if( tab_space > available_space ) + { + S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); + // subtract off reserved portion on left + available_width_with_arrows -= tabcntr_tab_partial_width; + + S32 running_tab_width = 0; + setMaxScrollPos(getTabCount()); + for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it) + { + running_tab_width += (*tab_it)->mButton->getRect().getWidth(); + if (running_tab_width > available_width_with_arrows) + { + break; + } + setMaxScrollPos(getMaxScrollPos()-1); + } + // in case last tab doesn't actually fit on screen, make it the last scrolling position + setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1)); + no_scroll = false; + } + } + if (no_scroll) + { + setMaxScrollPos(0); + setScrollPos(0); + } + if (getScrollPos() > getMaxScrollPos()) + { + setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead? + } +} + +void LLTabContainer::commitHoveredButton(S32 x, S32 y) +{ + if (!getTabsHidden() && hasMouseCapture()) + { + for (tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLButton* button = (*iter)->mButton; + LLPanel* panel = (*iter)->mTabPanel; + if (button->getEnabled() && button->getVisible() && !panel->getVisible()) + { + S32 local_x = x - button->getRect().mLeft; + S32 local_y = y - button->getRect().mBottom; + if (button->pointInView(local_x, local_y)) + { + button->onCommit(); + break; + } + } + } + } +} + +S32 LLTabContainer::getTotalTabWidth() const +{ + return mTotalTabWidth; +} + +void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible ) +{ + for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr ) + { + LLTabTuple const *pTT = *itr; + if( pTT->mTabPanel == aPanel ) + { + pTT->mVisible = aVisible; + break; + } + } + + bool foundTab( false ); + for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr ) + { + LLTabTuple const *pTT = *itr; + if( pTT->mVisible ) + { + this->selectTab( itr - mTabList.begin() ); + foundTab = true; + break; + } + } + + if( foundTab ) + this->setVisible( true ); + else + this->setVisible( false ); + + updateMaxScrollPos(); +} diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 1684b9570d..b22eec2fe5 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -1,330 +1,330 @@ -/** - * @file lltabcontainer.h - * @brief LLTabContainer class - * - * $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$ - */ - -#ifndef LL_TABCONTAINER_H -#define LL_TABCONTAINER_H - -#include "llpanel.h" -#include "lltextbox.h" -#include "llframetimer.h" -#include "lliconctrl.h" -#include "llbutton.h" - -class LLTabTuple; - -class LLTabContainer : public LLPanel -{ -public: - enum TabPosition - { - TOP, - BOTTOM, - LEFT - }; - typedef enum e_insertion_point - { - START, - END, - LEFT_OF_CURRENT, - RIGHT_OF_CURRENT - } eInsertionPoint; - - struct TabPositions : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - struct TabParams : public LLInitParam::Block - { - Optional tab_top_image_unselected, - tab_top_image_selected, - tab_top_image_flash, - tab_bottom_image_unselected, - tab_bottom_image_selected, - tab_bottom_image_flash, - tab_left_image_unselected, - tab_left_image_selected, - tab_left_image_flash; - TabParams(); - }; - - struct Params - : public LLInitParam::Block - { - Optional tab_position; - Optional tab_width, - tab_min_width, - tab_max_width, - tab_height, - label_pad_bottom, - label_pad_left; - - Optional hide_tabs; - Optional hide_scroll_arrows; - Optional tab_padding_right; - - Optional first_tab, - middle_tab, - last_tab; - - /** - * Tab label horizontal alignment - */ - Optional font_halign; - - /** - * Tab label ellipses - */ - Optional use_ellipses; - - /** - * Use LLCustomButtonIconCtrl or LLButton in LLTabTuple - */ - Optional use_custom_icon_ctrl; - - /** - * Open tabs on hover in drag and drop situations - */ - Optional open_tabs_on_drag_and_drop; - - /** - * Enable tab flashing - */ - Optional enable_tabs_flashing; - Optional tabs_flashing_color; - - /** - * Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true) - */ - Optional tab_icon_ctrl_pad; - - Optional use_tab_offset; - - Params(); - }; - -protected: - LLTabContainer(const Params&); - friend class LLUICtrlFactory; - -public: - //LLTabContainer( const std::string& name, const LLRect& rect, TabPosition pos, - // bool bordered, bool is_vertical); - - /*virtual*/ ~LLTabContainer(); - - // from LLView - /*virtual*/ void setValue(const LLSD& value); - - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); - /*virtual*/ void draw(); - /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask ); - /*virtual*/ bool handleMouseUp( S32 x, S32 y, MASK mask ); - /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleKeyHere(KEY key, MASK mask); - /*virtual*/ bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, - EDragAndDropType type, void* cargo_data, - EAcceptance* accept, std::string& tooltip); - /*virtual*/ LLView* getChildView(const std::string& name, bool recurse = true) const; - /*virtual*/ LLView* findChildView(const std::string& name, bool recurse = true) const; - /*virtual*/ void initFromParams(const LLPanel::Params& p); - /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); - /*virtual*/ bool postBuild(); - - struct TabPanelParams : public LLInitParam::Block - { - Mandatory panel; - - Optional label; - Optional select_tab, - is_placeholder; - Optional indent; - Optional insert_at; - Optional user_data; - - TabPanelParams() - : panel("panel", NULL), - label("label"), - select_tab("select_tab"), - is_placeholder("is_placeholder"), - indent("indent"), - insert_at("insert_at", END) - {} - }; - - void addTabPanel(LLPanel* panel); - void addTabPanel(const TabPanelParams& panel); - void addPlaceholder(LLPanel* child, const std::string& label); - void removeTabPanel( LLPanel* child ); - void lockTabs(S32 num_tabs = 0); - void unlockTabs(); - S32 getNumLockedTabs() { return mLockedTabCount; } - void enableTabButton(S32 which, bool enable); - void deleteAllTabs(); - LLPanel* getCurrentPanel(); - S32 getCurrentPanelIndex(); - S32 getTabCount(); - LLPanel* getPanelByIndex(S32 index); - S32 getIndexForPanel(LLPanel* panel); - S32 getPanelIndexByTitle(const std::string& title); - LLPanel* getPanelByName(const std::string& name); - S32 getTotalTabWidth() const; - void setCurrentTabName(const std::string& name); - - void selectFirstTab(); - void selectLastTab(); - void selectNextTab(); - void selectPrevTab(); - bool selectTabPanel( LLPanel* child ); - bool selectTab(S32 which); - bool selectTabByName(const std::string& title); - void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; } - - bool getTabPanelFlashing(LLPanel* child); - void setTabPanelFlashing(LLPanel* child, bool state); - void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white); - void setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white); - void setTabImage(LLPanel* child, LLIconCtrl* icon); - void setTitle( const std::string& title ); - const std::string getPanelTitle(S32 index); - - void setTopBorderHeight(S32 height); - S32 getTopBorderHeight() const; - - void setRightTabBtnOffset( S32 offset ); - void setPanelTitle(S32 index, const std::string& title); - - TabPosition getTabPosition() const { return mTabPosition; } - void setMinTabWidth(S32 width) { mMinTabWidth = width; } - void setMaxTabWidth(S32 width) { mMaxTabWidth = width; } - S32 getMinTabWidth() const { return mMinTabWidth; } - S32 getMaxTabWidth() const { return mMaxTabWidth; } - - void setTabVisibility( LLPanel const *aPanel, bool ); - - void startDragAndDropDelayTimer() { mDragAndDropDelayTimer.start(); } - - void onTabBtn( const LLSD& data, LLPanel* panel ); - void onNextBtn(const LLSD& data); - void onNextBtnHeld(const LLSD& data); - void onPrevBtn(const LLSD& data); - void onPrevBtnHeld(const LLSD& data); - void onJumpFirstBtn( const LLSD& data ); - void onJumpLastBtn( const LLSD& data ); - -private: - - void initButtons(); - - bool setTab(S32 which); - - LLTabTuple* getTab(S32 index) { return mTabList[index]; } - LLTabTuple* getTabByPanel(LLPanel* child); - void insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point); - - S32 getScrollPos() const { return mScrollPos; } - void setScrollPos(S32 pos) { mScrollPos = pos; } - S32 getMaxScrollPos() const { return mMaxScrollPos; } - void setMaxScrollPos(S32 pos) { mMaxScrollPos = pos; } - S32 getScrollPosPixels() const { return mScrollPosPixels; } - void setScrollPosPixels(S32 pixels) { mScrollPosPixels = pixels; } - - void setTabsHidden(bool hidden) { mTabsHidden = hidden; } - bool getTabsHidden() const { return mTabsHidden; } - - void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap - void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap - - void updateMaxScrollPos(); - void commitHoveredButton(S32 x, S32 y); - - // updates tab button images given the tuple, tab position and the corresponding params - void update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos); - void reshapeTuple(LLTabTuple* tuple); - - // Variables - - typedef std::vector tuple_list_t; - tuple_list_t mTabList; - - S32 mCurrentTabIdx; - bool mTabsHidden; - bool mHideScrollArrows; - - bool mScrolled; - LLFrameTimer mScrollTimer; - S32 mScrollPos; - S32 mScrollPosPixels; - S32 mMaxScrollPos; - - LLTextBox* mTitleBox; - - S32 mTopBorderHeight; - TabPosition mTabPosition; - S32 mLockedTabCount; - S32 mMinTabWidth; - LLButton* mPrevArrowBtn; - LLButton* mNextArrowBtn; - - bool mIsVertical; - - // Horizontal specific - LLButton* mJumpPrevArrowBtn; - LLButton* mJumpNextArrowBtn; - - S32 mRightTabBtnOffset; // Extra room to the right of the tab buttons. - - S32 mMaxTabWidth; - S32 mTotalTabWidth; - S32 mTabHeight; - - // Padding under the text labels of tab buttons - S32 mLabelPadBottom; - // Padding to the left of text labels of tab buttons - S32 mLabelPadLeft; - - LLFrameTimer mDragAndDropDelayTimer; - - LLFontGL::HAlign mFontHalign; - const LLFontGL* mFont; - - TabParams mFirstTabParams; - TabParams mMiddleTabParams; - TabParams mLastTabParams; - - bool mCustomIconCtrlUsed; - bool mOpenTabsOnDragAndDrop; - bool mEnableTabsFlashing; - LLUIColor mTabsFlashingColor; - S32 mTabIconCtrlPad; - bool mUseTabEllipses; - LLFrameTimer mMouseDownTimer; - - bool mUseTabOffset; -}; - -#endif // LL_TABCONTAINER_H +/** + * @file lltabcontainer.h + * @brief LLTabContainer class + * + * $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$ + */ + +#ifndef LL_TABCONTAINER_H +#define LL_TABCONTAINER_H + +#include "llpanel.h" +#include "lltextbox.h" +#include "llframetimer.h" +#include "lliconctrl.h" +#include "llbutton.h" + +class LLTabTuple; + +class LLTabContainer : public LLPanel +{ +public: + enum TabPosition + { + TOP, + BOTTOM, + LEFT + }; + typedef enum e_insertion_point + { + START, + END, + LEFT_OF_CURRENT, + RIGHT_OF_CURRENT + } eInsertionPoint; + + struct TabPositions : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + struct TabParams : public LLInitParam::Block + { + Optional tab_top_image_unselected, + tab_top_image_selected, + tab_top_image_flash, + tab_bottom_image_unselected, + tab_bottom_image_selected, + tab_bottom_image_flash, + tab_left_image_unselected, + tab_left_image_selected, + tab_left_image_flash; + TabParams(); + }; + + struct Params + : public LLInitParam::Block + { + Optional tab_position; + Optional tab_width, + tab_min_width, + tab_max_width, + tab_height, + label_pad_bottom, + label_pad_left; + + Optional hide_tabs; + Optional hide_scroll_arrows; + Optional tab_padding_right; + + Optional first_tab, + middle_tab, + last_tab; + + /** + * Tab label horizontal alignment + */ + Optional font_halign; + + /** + * Tab label ellipses + */ + Optional use_ellipses; + + /** + * Use LLCustomButtonIconCtrl or LLButton in LLTabTuple + */ + Optional use_custom_icon_ctrl; + + /** + * Open tabs on hover in drag and drop situations + */ + Optional open_tabs_on_drag_and_drop; + + /** + * Enable tab flashing + */ + Optional enable_tabs_flashing; + Optional tabs_flashing_color; + + /** + * Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true) + */ + Optional tab_icon_ctrl_pad; + + Optional use_tab_offset; + + Params(); + }; + +protected: + LLTabContainer(const Params&); + friend class LLUICtrlFactory; + +public: + //LLTabContainer( const std::string& name, const LLRect& rect, TabPosition pos, + // bool bordered, bool is_vertical); + + /*virtual*/ ~LLTabContainer(); + + // from LLView + /*virtual*/ void setValue(const LLSD& value); + + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); + /*virtual*/ void draw(); + /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleMouseUp( S32 x, S32 y, MASK mask ); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleKeyHere(KEY key, MASK mask); + /*virtual*/ bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, + EDragAndDropType type, void* cargo_data, + EAcceptance* accept, std::string& tooltip); + /*virtual*/ LLView* getChildView(const std::string& name, bool recurse = true) const; + /*virtual*/ LLView* findChildView(const std::string& name, bool recurse = true) const; + /*virtual*/ void initFromParams(const LLPanel::Params& p); + /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); + /*virtual*/ bool postBuild(); + + struct TabPanelParams : public LLInitParam::Block + { + Mandatory panel; + + Optional label; + Optional select_tab, + is_placeholder; + Optional indent; + Optional insert_at; + Optional user_data; + + TabPanelParams() + : panel("panel", NULL), + label("label"), + select_tab("select_tab"), + is_placeholder("is_placeholder"), + indent("indent"), + insert_at("insert_at", END) + {} + }; + + void addTabPanel(LLPanel* panel); + void addTabPanel(const TabPanelParams& panel); + void addPlaceholder(LLPanel* child, const std::string& label); + void removeTabPanel( LLPanel* child ); + void lockTabs(S32 num_tabs = 0); + void unlockTabs(); + S32 getNumLockedTabs() { return mLockedTabCount; } + void enableTabButton(S32 which, bool enable); + void deleteAllTabs(); + LLPanel* getCurrentPanel(); + S32 getCurrentPanelIndex(); + S32 getTabCount(); + LLPanel* getPanelByIndex(S32 index); + S32 getIndexForPanel(LLPanel* panel); + S32 getPanelIndexByTitle(const std::string& title); + LLPanel* getPanelByName(const std::string& name); + S32 getTotalTabWidth() const; + void setCurrentTabName(const std::string& name); + + void selectFirstTab(); + void selectLastTab(); + void selectNextTab(); + void selectPrevTab(); + bool selectTabPanel( LLPanel* child ); + bool selectTab(S32 which); + bool selectTabByName(const std::string& title); + void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; } + + bool getTabPanelFlashing(LLPanel* child); + void setTabPanelFlashing(LLPanel* child, bool state); + void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white); + void setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white); + void setTabImage(LLPanel* child, LLIconCtrl* icon); + void setTitle( const std::string& title ); + const std::string getPanelTitle(S32 index); + + void setTopBorderHeight(S32 height); + S32 getTopBorderHeight() const; + + void setRightTabBtnOffset( S32 offset ); + void setPanelTitle(S32 index, const std::string& title); + + TabPosition getTabPosition() const { return mTabPosition; } + void setMinTabWidth(S32 width) { mMinTabWidth = width; } + void setMaxTabWidth(S32 width) { mMaxTabWidth = width; } + S32 getMinTabWidth() const { return mMinTabWidth; } + S32 getMaxTabWidth() const { return mMaxTabWidth; } + + void setTabVisibility( LLPanel const *aPanel, bool ); + + void startDragAndDropDelayTimer() { mDragAndDropDelayTimer.start(); } + + void onTabBtn( const LLSD& data, LLPanel* panel ); + void onNextBtn(const LLSD& data); + void onNextBtnHeld(const LLSD& data); + void onPrevBtn(const LLSD& data); + void onPrevBtnHeld(const LLSD& data); + void onJumpFirstBtn( const LLSD& data ); + void onJumpLastBtn( const LLSD& data ); + +private: + + void initButtons(); + + bool setTab(S32 which); + + LLTabTuple* getTab(S32 index) { return mTabList[index]; } + LLTabTuple* getTabByPanel(LLPanel* child); + void insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point); + + S32 getScrollPos() const { return mScrollPos; } + void setScrollPos(S32 pos) { mScrollPos = pos; } + S32 getMaxScrollPos() const { return mMaxScrollPos; } + void setMaxScrollPos(S32 pos) { mMaxScrollPos = pos; } + S32 getScrollPosPixels() const { return mScrollPosPixels; } + void setScrollPosPixels(S32 pixels) { mScrollPosPixels = pixels; } + + void setTabsHidden(bool hidden) { mTabsHidden = hidden; } + bool getTabsHidden() const { return mTabsHidden; } + + void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap + void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap + + void updateMaxScrollPos(); + void commitHoveredButton(S32 x, S32 y); + + // updates tab button images given the tuple, tab position and the corresponding params + void update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos); + void reshapeTuple(LLTabTuple* tuple); + + // Variables + + typedef std::vector tuple_list_t; + tuple_list_t mTabList; + + S32 mCurrentTabIdx; + bool mTabsHidden; + bool mHideScrollArrows; + + bool mScrolled; + LLFrameTimer mScrollTimer; + S32 mScrollPos; + S32 mScrollPosPixels; + S32 mMaxScrollPos; + + LLTextBox* mTitleBox; + + S32 mTopBorderHeight; + TabPosition mTabPosition; + S32 mLockedTabCount; + S32 mMinTabWidth; + LLButton* mPrevArrowBtn; + LLButton* mNextArrowBtn; + + bool mIsVertical; + + // Horizontal specific + LLButton* mJumpPrevArrowBtn; + LLButton* mJumpNextArrowBtn; + + S32 mRightTabBtnOffset; // Extra room to the right of the tab buttons. + + S32 mMaxTabWidth; + S32 mTotalTabWidth; + S32 mTabHeight; + + // Padding under the text labels of tab buttons + S32 mLabelPadBottom; + // Padding to the left of text labels of tab buttons + S32 mLabelPadLeft; + + LLFrameTimer mDragAndDropDelayTimer; + + LLFontGL::HAlign mFontHalign; + const LLFontGL* mFont; + + TabParams mFirstTabParams; + TabParams mMiddleTabParams; + TabParams mLastTabParams; + + bool mCustomIconCtrlUsed; + bool mOpenTabsOnDragAndDrop; + bool mEnableTabsFlashing; + LLUIColor mTabsFlashingColor; + S32 mTabIconCtrlPad; + bool mUseTabEllipses; + LLFrameTimer mMouseDownTimer; + + bool mUseTabOffset; +}; + +#endif // LL_TABCONTAINER_H diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index f6dd38bce5..1249461be9 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1,3890 +1,3890 @@ -/** - * @file lltextbase.cpp - * @author Martin Reddy - * @brief The base class of text box/editor, providing Url handling support - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2009-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 "lltextbase.h" - -#include "llemojidictionary.h" -#include "llemojihelper.h" -#include "lllocalcliprect.h" -#include "llmenugl.h" -#include "llscrollcontainer.h" -#include "llspellcheck.h" -#include "llstl.h" -#include "lltextparser.h" -#include "lltextutil.h" -#include "lltooltip.h" -#include "lltrans.h" -#include "lluictrl.h" -#include "llurlaction.h" -#include "llurlregistry.h" -#include "llview.h" -#include "llwindow.h" -#include - -const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds -const S32 CURSOR_THICKNESS = 2; -const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. - -LLTextBase::line_info::line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num) -: mDocIndexStart(index_start), - mDocIndexEnd(index_end), - mRect(rect), - mLineNum(line_num) -{} - -bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const -{ - // sort empty spans (e.g. 11-11) after previous non-empty spans (e.g. 5-11) - if (a->getEnd() == b->getEnd()) - { - return a->getStart() < b->getStart(); - } - else - { - return a->getEnd() < b->getEnd(); - } -} - - -// helper functors -bool LLTextBase::compare_bottom::operator()(const S32& a, const LLTextBase::line_info& b) const -{ - return a > b.mRect.mBottom; // bottom of a is higher than bottom of b -} - -bool LLTextBase::compare_bottom::operator()(const LLTextBase::line_info& a, const S32& b) const -{ - return a.mRect.mBottom > b; // bottom of a is higher than bottom of b -} - -bool LLTextBase::compare_bottom::operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const -{ - return a.mRect.mBottom > b.mRect.mBottom; // bottom of a is higher than bottom of b -} - -// helper functors -bool LLTextBase::compare_top::operator()(const S32& a, const LLTextBase::line_info& b) const -{ - return a > b.mRect.mTop; // top of a is higher than top of b -} - -bool LLTextBase::compare_top::operator()(const LLTextBase::line_info& a, const S32& b) const -{ - return a.mRect.mTop > b; // top of a is higher than top of b -} - -bool LLTextBase::compare_top::operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const -{ - return a.mRect.mTop > b.mRect.mTop; // top of a is higher than top of b -} - -struct LLTextBase::line_end_compare -{ - bool operator()(const S32& pos, const LLTextBase::line_info& info) const - { - return (pos < info.mDocIndexEnd); - } - - bool operator()(const LLTextBase::line_info& info, const S32& pos) const - { - return (info.mDocIndexEnd < pos); - } - - bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const - { - return (a.mDocIndexEnd < b.mDocIndexEnd); - } - -}; - -////////////////////////////////////////////////////////////////////////// -// -// LLTextBase -// - -// register LLTextBase::Params under name "textbase" -static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase"); - -LLTextBase::LineSpacingParams::LineSpacingParams() -: multiple("multiple", 1.f), - pixels("pixels", 0) -{ -} - - -LLTextBase::Params::Params() -: cursor_color("cursor_color"), - text_color("text_color"), - text_readonly_color("text_readonly_color"), - text_tentative_color("text_tentative_color"), - bg_visible("bg_visible", false), - border_visible("border_visible", false), - bg_readonly_color("bg_readonly_color"), - bg_writeable_color("bg_writeable_color"), - bg_focus_color("bg_focus_color"), - text_selected_color("text_selected_color"), - bg_selected_color("bg_selected_color"), - allow_scroll("allow_scroll", true), - plain_text("plain_text",false), - track_end("track_end", false), - read_only("read_only", false), - skip_link_underline("skip_link_underline", false), - spellcheck("spellcheck", false), - v_pad("v_pad", 0), - h_pad("h_pad", 0), - clip("clip", true), - clip_partial("clip_partial", true), - line_spacing("line_spacing"), - max_text_length("max_length", 255), - font_shadow("font_shadow"), - text_valign("text_valign"), - wrap("wrap"), - trusted_content("trusted_content", true), - always_show_icons("always_show_icons", false), - use_ellipses("use_ellipses", false), - use_emoji("use_emoji", true), - use_color("use_color", true), - parse_urls("parse_urls", false), - force_urls_external("force_urls_external", false), - parse_highlights("parse_highlights", false) -{ - addSynonym(track_end, "track_bottom"); - addSynonym(wrap, "word_wrap"); - addSynonym(parse_urls, "allow_html"); -} - - -LLTextBase::LLTextBase(const LLTextBase::Params &p) -: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), - mURLClickSignal(NULL), - mIsFriendSignal(NULL), - mIsObjectBlockedSignal(NULL), - mMaxTextByteLength( p.max_text_length ), - mFont(p.font), - mFontShadow(p.font_shadow), - mPopupMenuHandle(), - mReadOnly(p.read_only), - mSkipTripleClick(false), - mSkipLinkUnderline(p.skip_link_underline), - mSpellCheck(p.spellcheck), - mSpellCheckStart(-1), - mSpellCheckEnd(-1), - mCursorColor(p.cursor_color), - mFgColor(p.text_color), - mBorderVisible( p.border_visible ), - mReadOnlyFgColor(p.text_readonly_color), - mTentativeFgColor(p.text_tentative_color()), - mWriteableBgColor(p.bg_writeable_color), - mReadOnlyBgColor(p.bg_readonly_color), - mFocusBgColor(p.bg_focus_color), - mTextSelectedColor(p.text_selected_color), - mSelectedBGColor(p.bg_selected_color), - mReflowIndex(S32_MAX), - mCursorPos( 0 ), - mScrollNeeded(false), - mDesiredXPixel(-1), - mHPad(p.h_pad), - mVPad(p.v_pad), - mHAlign(p.font_halign), - mVAlign(p.font_valign), - mTextVAlign(p.text_valign.isProvided() ? p.text_valign.getValue() : p.font_valign.getValue()), - mLineSpacingMult(p.line_spacing.multiple), - mLineSpacingPixels(p.line_spacing.pixels), - mClip(p.clip), - mClipPartial(p.clip_partial && !p.allow_scroll), - mTrustedContent(p.trusted_content), - mAlwaysShowIcons(p.always_show_icons), - mTrackEnd( p.track_end ), - mScrollIndex(-1), - mSelectionStart( 0 ), - mSelectionEnd( 0 ), - mIsSelecting( false ), - mPlainText ( p.plain_text ), - mWordWrap(p.wrap), - mUseEllipses( p.use_ellipses ), - mUseEmoji(p.use_emoji), - mUseColor(p.use_color), - mParseHTML(p.parse_urls), - mForceUrlsExternal(p.force_urls_external), - mParseHighlights(p.parse_highlights), - mBGVisible(p.bg_visible), - mScroller(NULL), - mStyleDirty(true) -{ - if(p.allow_scroll) - { - LLScrollContainer::Params scroll_params; - scroll_params.name = "text scroller"; - scroll_params.rect = getLocalRect(); - scroll_params.follows.flags = FOLLOWS_ALL; - scroll_params.is_opaque = false; - scroll_params.mouse_opaque = false; - scroll_params.min_auto_scroll_rate = 200; - scroll_params.max_auto_scroll_rate = 800; - scroll_params.border_visible = p.border_visible; - mScroller = LLUICtrlFactory::create(scroll_params); - addChild(mScroller); - } - - LLView::Params view_params; - view_params.name = "text_contents"; - view_params.rect = LLRect(0, 500, 500, 0); - view_params.mouse_opaque = false; - - mDocumentView = LLUICtrlFactory::create(view_params); - if (mScroller) - { - mScroller->addChild(mDocumentView); - } - else - { - addChild(mDocumentView); - } - - if (mSpellCheck) - { - LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLTextBase::onSpellCheckSettingsChange, this)); - } - mSpellCheckTimer.reset(); - - createDefaultSegment(); - - updateRects(); -} - -LLTextBase::~LLTextBase() -{ - mSegments.clear(); - LLContextMenu* menu = static_cast(mPopupMenuHandle.get()); - if (menu) - { - menu->die(); - mPopupMenuHandle.markDead(); - } - delete mURLClickSignal; - delete mIsFriendSignal; - delete mIsObjectBlockedSignal; -} - -void LLTextBase::initFromParams(const LLTextBase::Params& p) -{ - LLUICtrl::initFromParams(p); - resetDirty(); // Update saved text state - updateSegments(); - - // HACK: work around enabled == readonly design bug -- RN - // setEnabled will modify our read only status, so do this after - // LLTextBase::initFromParams - if (p.read_only.isProvided()) - { - mReadOnly = p.read_only; - } -} - -bool LLTextBase::truncate() -{ - bool did_truncate = false; - - // First rough check - if we're less than 1/4th the size, we're OK - if (getLength() >= S32(mMaxTextByteLength / 4)) - { - // Have to check actual byte size - S32 utf8_byte_size = 0; - LLSD value = getViewModel()->getValue(); - if (value.type() == LLSD::TypeString) - { - // save a copy for strings. - utf8_byte_size = value.size(); - } - else - { - // non string LLSDs need explicit conversion to string - utf8_byte_size = value.asString().size(); - } - - if ( utf8_byte_size > mMaxTextByteLength ) - { - // Truncate safely in UTF-8 - std::string temp_utf8_text = value.asString(); - temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); - LLWString text = utf8str_to_wstring( temp_utf8_text ); - // remove extra bit of current string, to preserve formatting, etc. - removeStringNoUndo(text.size(), getWText().size() - text.size()); - did_truncate = true; - } - } - - return did_truncate; -} - -const LLStyle::Params& LLTextBase::getStyleParams() -{ - //FIXME: convert mDefaultStyle to a flyweight http://www.boost.org/doc/libs/1_40_0/libs/flyweight/doc/index.html - //and eliminate color member values - if (mStyleDirty) - { - mStyle - .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor - .readonly_color(LLUIColor(&mReadOnlyFgColor)) - .selected_color(LLUIColor(&mTextSelectedColor)) - .font(mFont) - .drop_shadow(mFontShadow); - mStyleDirty = false; - } - return mStyle; -} - -void LLTextBase::beforeValueChange() -{ - -} - -void LLTextBase::onValueChange(S32 start, S32 end) -{ -} - -std::vector LLTextBase::getSelectionRects() -{ - // Nor supposed to be called without selection - llassert(hasSelection()); - llassert(!mLineInfoList.empty()); - - std::vector selection_rects; - - S32 selection_left = llmin(mSelectionStart, mSelectionEnd); - S32 selection_right = llmax(mSelectionStart, mSelectionEnd); - - // Skip through the lines we aren't drawing. - LLRect content_display_rect = getVisibleDocumentRect(); - - // binary search for line that starts before top of visible buffer - line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); - line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); - - bool done = false; - - // Find the coordinates of the selected area - for (; line_iter != end_iter && !done; ++line_iter) - { - // is selection visible on this line? - if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) - { - segment_set_t::iterator segment_iter; - S32 segment_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); - - // Use F32 otherwise a string of multiple segments - // will accumulate a large error - F32 left_precise = line_iter->mRect.mLeft; - F32 right_precise = line_iter->mRect.mLeft; - - for (; segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) - { - LLTextSegmentPtr segmentp = *segment_iter; - - S32 segment_line_start = segmentp->getStart() + segment_offset; - S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); - - if (segment_line_start > segment_line_end) break; - - F32 segment_width = 0; - S32 segment_height = 0; - - // if selection after beginning of segment - if (selection_left >= segment_line_start) - { - S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; - segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); - left_precise += segment_width; - } - - // if selection_right == segment_line_end then that means we are the first character of the next segment - // or first character of the next line, in either case we want to add the length of the current segment - // to the selection rectangle and continue. - // if selection right > segment_line_end then selection spans end of current segment... - if (selection_right >= segment_line_end) - { - // extend selection slightly beyond end of line - // to indicate selection of newline character (use "n" character to determine width) - S32 num_chars = segment_line_end - segment_line_start; - segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); - right_precise += segment_width; - } - // else if selection ends on current segment... - else - { - S32 num_chars = selection_right - segment_line_start; - segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); - right_precise += segment_width; - - break; - } - } - - LLRect selection_rect; - selection_rect.mLeft = left_precise; - selection_rect.mRight = right_precise; - selection_rect.mBottom = line_iter->mRect.mBottom; - selection_rect.mTop = line_iter->mRect.mTop; - - selection_rects.push_back(selection_rect); - } - } - - return selection_rects; -} - -// Draws the black box behind the selected text -void LLTextBase::drawSelectionBackground() -{ - // Draw selection even if we don't have keyboard focus for search/replace - if (hasSelection() && !mLineInfoList.empty()) - { - std::vector selection_rects = getSelectionRects(); - - // Draw the selection box (we're using a box instead of reversing the colors on the selected text). - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - const LLColor4& color = mSelectedBGColor; - F32 alpha = hasFocus() ? 0.7f : 0.3f; - alpha *= getDrawContext().mAlpha; - - LLColor4 selection_color(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], alpha); - LLRect content_display_rect = getVisibleDocumentRect(); - - for (std::vector::iterator rect_it = selection_rects.begin(); - rect_it != selection_rects.end(); - ++rect_it) - { - LLRect selection_rect = *rect_it; - if (mScroller) - { - // If scroller is On content_display_rect has correct rect and safe to use as is - // Note: we might need to account for border - selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom); - } - else - { - // If scroller is Off content_display_rect will have rect from document, adjusted to text width, heigh and position - // and we have to acount for offset depending on position - S32 v_delta = 0; - S32 h_delta = 0; - switch (mVAlign) - { - case LLFontGL::TOP: - v_delta = mVisibleTextRect.mTop - content_display_rect.mTop - mVPad; - break; - case LLFontGL::VCENTER: - v_delta = (llmax(mVisibleTextRect.getHeight() - content_display_rect.mTop, -content_display_rect.mBottom) + (mVisibleTextRect.mBottom - content_display_rect.mBottom)) / 2; - break; - case LLFontGL::BOTTOM: - v_delta = mVisibleTextRect.mBottom - content_display_rect.mBottom; - break; - default: - break; - } - switch (mHAlign) - { - case LLFontGL::LEFT: - h_delta = mVisibleTextRect.mLeft - content_display_rect.mLeft + mHPad; - break; - case LLFontGL::HCENTER: - h_delta = (llmax(mVisibleTextRect.getWidth() - content_display_rect.mLeft, -content_display_rect.mRight) + (mVisibleTextRect.mRight - content_display_rect.mRight)) / 2; - break; - case LLFontGL::RIGHT: - h_delta = mVisibleTextRect.mRight - content_display_rect.mRight; - break; - default: - break; - } - selection_rect.translate(h_delta, v_delta); - } - gl_rect_2d(selection_rect, selection_color); - } - } -} - -void LLTextBase::drawCursor() -{ - F32 alpha = getDrawContext().mAlpha; - - if( hasFocus() - && gFocusMgr.getAppHasFocus() - && !mReadOnly) - { - const LLWString &wtext = getWText(); - const llwchar* text = wtext.c_str(); - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - cursor_rect.translate(-1, 0); - segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos); - - // take style from last segment - LLTextSegmentPtr segmentp; - - if (seg_it != mSegments.end()) - { - segmentp = *seg_it; - } - else - { - return; - } - - // Draw the cursor - // (Flash the cursor every half second starting a fixed time after the last keystroke) - F32 elapsed = mCursorBlinkTimer.getElapsedTimeF32(); - if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) - { - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) - { - S32 segment_width = 0; - S32 segment_height = 0; - segmentp->getDimensions(mCursorPos - segmentp->getStart(), 1, segment_width, segment_height); - S32 width = llmax(CURSOR_THICKNESS, segment_width); - cursor_rect.mRight = cursor_rect.mLeft + width; - } - else - { - cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS; - } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLColor4 cursor_color = mCursorColor.get() % alpha; - gGL.color4fv( cursor_color.mV ); - - gl_rect_2d(cursor_rect); - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') - { - LLColor4 text_color; - const LLFontGL* fontp; - text_color = segmentp->getColor(); - fontp = segmentp->getStyle()->getFont(); - fontp->render(text, mCursorPos, cursor_rect, - LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha), - LLFontGL::LEFT, mTextVAlign, - LLFontGL::NORMAL, - LLFontGL::NO_SHADOW, - 1); - } - - // Make sure the IME is in the right place - LLRect screen_pos = calcScreenRect(); - LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) ); - - ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]); - ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]); - getWindow()->setLanguageTextInput( ime_pos ); - } - } -} - -void LLTextBase::drawText() -{ - S32 text_len = getLength(); - - if (text_len <= 0 && mLabel.empty()) - { - return; - } - else if (useLabel()) - { - text_len = mLabel.getWString().length(); - } - - S32 selection_left = -1; - S32 selection_right = -1; - // Draw selection even if we don't have keyboard focus for search/replace - if( hasSelection()) - { - selection_left = llmin( mSelectionStart, mSelectionEnd ); - selection_right = llmax( mSelectionStart, mSelectionEnd ); - } - - std::pair line_range = getVisibleLines(mClipPartial); - S32 first_line = line_range.first; - S32 last_line = line_range.second; - if (first_line >= last_line) - { - return; - } - - S32 line_start = getLineStart(first_line); - // find first text segment that spans top of visible portion of text buffer - segment_set_t::iterator seg_iter = getSegIterContaining(line_start); - if (seg_iter == mSegments.end()) - { - return; - } - - // Perform spell check if needed - if ( (getSpellCheck()) && (getWText().length() > 2) ) - { - // Calculate start and end indices for the spell checking range - S32 start = line_start; - S32 end = getLineEnd(last_line); - - if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) ) - { - const LLWString& wstrText = getWText(); - mMisspellRanges.clear(); - - segment_set_t::const_iterator seg_it = getSegIterContaining(start); - while (mSegments.end() != seg_it) - { - LLTextSegmentPtr text_segment = *seg_it; - if ( (text_segment.isNull()) || (text_segment->getStart() >= end) ) - { - break; - } - - if (!text_segment->canEdit()) - { - ++seg_it; - continue; - } - - // Combine adjoining text segments into one - U32 seg_start = text_segment->getStart(), seg_end = llmin(text_segment->getEnd(), end); - while (mSegments.end() != ++seg_it) - { - text_segment = *seg_it; - if ( (text_segment.isNull()) || (!text_segment->canEdit()) || (text_segment->getStart() >= end) ) - { - break; - } - seg_end = llmin(text_segment->getEnd(), end); - } - - // Find the start of the first word - U32 word_start = seg_start, word_end = -1; - U32 text_length = wstrText.length(); - while ( (word_start < text_length) && (!LLStringOps::isAlpha(wstrText[word_start])) ) - { - word_start++; - } - - // Iterate over all words in the text block and check them one by one - while (word_start < seg_end) - { - // Find the end of the current word (special case handling for "'" when it's used as a contraction) - word_end = word_start + 1; - while ( (word_end < seg_end) && - ((LLWStringUtil::isPartOfWord(wstrText[word_end])) || - ((L'\'' == wstrText[word_end]) && - (LLStringOps::isAlnum(wstrText[word_end - 1])) && (LLStringOps::isAlnum(wstrText[word_end + 1])))) ) - { - word_end++; - } - if (word_end > seg_end) - { - break; - } - - if (word_start < text_length && word_end <= text_length && word_end > word_start) - { - std::string word = wstring_to_utf8str(wstrText.substr(word_start, word_end - word_start)); - - // Don't process words shorter than 3 characters - if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) - { - mMisspellRanges.push_back(std::pair(word_start, word_end)); - } - } - - // Find the start of the next word - word_start = word_end + 1; - while ( (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) ) - { - word_start++; - } - } - } - - mSpellCheckStart = start; - mSpellCheckEnd = end; - } - } - else - { - mMisspellRanges.clear(); - } - - LLTextSegmentPtr cur_segment = *seg_iter; - - std::list >::const_iterator misspell_it = std::lower_bound(mMisspellRanges.begin(), mMisspellRanges.end(), std::pair(line_start, 0)); - for (S32 cur_line = first_line; cur_line < last_line; cur_line++) - { - S32 next_line = cur_line + 1; - line_info& line = mLineInfoList[cur_line]; - - S32 next_start = -1; - S32 line_end = text_len; - - if (next_line < getLineCount()) - { - next_start = getLineStart(next_line); - line_end = next_start; - } - - LLRectf text_rect(line.mRect.mLeft, line.mRect.mTop, line.mRect.mRight, line.mRect.mBottom); - text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents - text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position - - // draw a single line of text - S32 seg_start = line_start; - while( seg_start < line_end ) - { - while( cur_segment->getEnd() <= seg_start ) - { - seg_iter++; - if (seg_iter == mSegments.end()) - { - LL_WARNS() << "Ran off the segmentation end!" << LL_ENDL; - - return; - } - cur_segment = *seg_iter; - } - - S32 seg_end = llmin(line_end, cur_segment->getEnd()); - S32 clipped_end = seg_end - cur_segment->getStart(); - - if (mUseEllipses // using ellipses - && clipped_end == line_end // last segment on line - && next_line == last_line // this is the last visible line - && last_line < (S32)mLineInfoList.size()) // and there is more text to display - { - // more lines of text to go, but we can't fit them - // so shrink text rect to force ellipses - text_rect.mRight -= 2; - } - - // Draw squiggly lines under any visible misspelled words - while ( (mMisspellRanges.end() != misspell_it) && (misspell_it->first < seg_end) && (misspell_it->second > seg_start) ) - { - // Skip the current word if the user is still busy editing it - if ( (!mSpellCheckTimer.hasExpired()) && (misspell_it->first <= (U32)mCursorPos) && (misspell_it->second >= (U32)mCursorPos) ) - { - ++misspell_it; - continue; - } - - U32 misspell_start = llmax(misspell_it->first, seg_start), misspell_end = llmin(misspell_it->second, seg_end); - S32 squiggle_start = 0, squiggle_end = 0, pony = 0; - cur_segment->getDimensions(seg_start - cur_segment->getStart(), misspell_start - seg_start, squiggle_start, pony); - cur_segment->getDimensions(misspell_start - cur_segment->getStart(), misspell_end - misspell_start, squiggle_end, pony); - squiggle_start += text_rect.mLeft; - - pony = (squiggle_end + 3) / 6; - squiggle_start += squiggle_end / 2 - pony * 3; - squiggle_end = squiggle_start + pony * 6; - - S32 squiggle_bottom = text_rect.mBottom + (S32)cur_segment->getStyle()->getFont()->getDescenderHeight(); - - gGL.color4ub(255, 0, 0, 200); - while (squiggle_start + 1 < squiggle_end) - { - gl_line_2d(squiggle_start, squiggle_bottom, squiggle_start + 2, squiggle_bottom - 2); - if (squiggle_start + 3 < squiggle_end) - { - gl_line_2d(squiggle_start + 2, squiggle_bottom - 3, squiggle_start + 4, squiggle_bottom - 1); - } - squiggle_start += 4; - } - - if (misspell_it->second > seg_end) - { - break; - } - ++misspell_it; - } - - text_rect.mLeft = cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect); - - seg_start = clipped_end + cur_segment->getStart(); - } - - line_start = next_start; - } -} - -/////////////////////////////////////////////////////////////////// -// Returns change in number of characters in mWText - -S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments ) -{ - beforeValueChange(); - - S32 old_len = getLength(); // length() returns character length - S32 insert_len = wstr.length(); - - pos = getEditableIndex(pos, true); - if (pos > old_len) - { - pos = old_len; - // Should not happen, - // if you encounter this, check where wrong position comes from - llassert(false); - } - - segment_set_t::iterator seg_iter = getEditableSegIterContaining(pos); - - LLTextSegmentPtr default_segment; - - LLTextSegmentPtr segmentp; - if (seg_iter != mSegments.end()) - { - segmentp = *seg_iter; - } - else - { - //segmentp = mSegments.back(); - return pos; - } - - if (segmentp->canEdit()) - { - segmentp->setEnd(segmentp->getEnd() + insert_len); - if (seg_iter != mSegments.end()) - { - ++seg_iter; - } - } - else - { - // create default editable segment to hold new text - LLStyleConstSP sp(new LLStyle(getStyleParams())); - default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this); - } - - // shift remaining segments to right - for(;seg_iter != mSegments.end(); ++seg_iter) - { - LLTextSegmentPtr segmentp = *seg_iter; - segmentp->setStart(segmentp->getStart() + insert_len); - segmentp->setEnd(segmentp->getEnd() + insert_len); - } - - // insert new segments - if (segments) - { - if (default_segment.notNull()) - { - // potentially overwritten by segments passed in - insertSegment(default_segment); - } - for (segment_vec_t::iterator seg_iter = segments->begin(); - seg_iter != segments->end(); - ++seg_iter) - { - LLTextSegment* segmentp = *seg_iter; - insertSegment(segmentp); - } - } - - // Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us) - if (mUseEmoji) - { - LLStyleSP emoji_style; - LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL; - for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++) - { - llwchar code = wstr[text_kitty]; - bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code); - if (isEmoji) - { - if (!emoji_style) - { - emoji_style = new LLStyle(getStyleParams()); - emoji_style->setFont(LLFontGL::getFontEmojiLarge()); - } - - S32 new_seg_start = pos + text_kitty; - insertSegment(new LLEmojiTextSegment(emoji_style, new_seg_start, new_seg_start + 1, *this)); - } - } - } - - getViewModel()->getEditableDisplay().insert(pos, wstr); - - if ( truncate() ) - { - insert_len = getLength() - old_len; - } - - onValueChange(pos, pos + insert_len); - needsReflow(pos); - - return insert_len; -} - -S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) -{ - - beforeValueChange(); - segment_set_t::iterator seg_iter = getSegIterContaining(pos); - while(seg_iter != mSegments.end()) - { - LLTextSegmentPtr segmentp = *seg_iter; - S32 end = pos + length; - if (segmentp->getStart() < pos) - { - // deleting from middle of segment - if (segmentp->getEnd() > end) - { - segmentp->setEnd(segmentp->getEnd() - length); - } - // truncating segment - else - { - segmentp->setEnd(pos); - } - } - else if (segmentp->getStart() < end) - { - // deleting entire segment - if (segmentp->getEnd() <= end) - { - // remove segment - segmentp->unlinkFromDocument(this); - segment_set_t::iterator seg_to_erase(seg_iter++); - mSegments.erase(seg_to_erase); - continue; - } - // deleting head of segment - else - { - segmentp->setStart(pos); - segmentp->setEnd(segmentp->getEnd() - length); - } - } - else - { - // shifting segments backward to fill deleted portion - segmentp->setStart(segmentp->getStart() - length); - segmentp->setEnd(segmentp->getEnd() - length); - } - ++seg_iter; - } - - getViewModel()->getEditableDisplay().erase(pos, length); - - // recreate default segment in case we erased everything - createDefaultSegment(); - - onValueChange(pos, pos); - needsReflow(pos); - - return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length -} - -S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc) -{ - beforeValueChange(); - - if (pos > (S32)getLength()) - { - return 0; - } - getViewModel()->getEditableDisplay()[pos] = wc; - - onValueChange(pos, pos + 1); - needsReflow(pos); - - return 1; -} - - -void LLTextBase::createDefaultSegment() -{ - // ensures that there is always at least one segment - if (mSegments.empty()) - { - LLStyleConstSP sp(new LLStyle(getStyleParams())); - LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this); - mSegments.insert(default_segment); - default_segment->linkToDocument(this); - } -} - -void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert) -{ - if (segment_to_insert.isNull()) - { - return; - } - - segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart()); - S32 reflow_start_index = 0; - - if (cur_seg_iter == mSegments.end()) - { - mSegments.insert(segment_to_insert); - segment_to_insert->linkToDocument(this); - reflow_start_index = segment_to_insert->getStart(); - } - else - { - LLTextSegmentPtr cur_segmentp = *cur_seg_iter; - reflow_start_index = cur_segmentp->getStart(); - if (cur_segmentp->getStart() < segment_to_insert->getStart()) - { - S32 old_segment_end = cur_segmentp->getEnd(); - // split old at start point for new segment - cur_segmentp->setEnd(segment_to_insert->getStart()); - // advance to next segment - // insert remainder of old segment - LLStyleConstSP sp = cur_segmentp->getStyle(); - LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( sp, segment_to_insert->getStart(), old_segment_end, *this); - mSegments.insert(cur_seg_iter, remainder_segment); - remainder_segment->linkToDocument(this); - // insert new segment before remainder of old segment - mSegments.insert(cur_seg_iter, segment_to_insert); - - segment_to_insert->linkToDocument(this); - // at this point, there will be two overlapping segments owning the text - // associated with the incoming segment - } - else - { - mSegments.insert(cur_seg_iter, segment_to_insert); - segment_to_insert->linkToDocument(this); - } - - // now delete/truncate remaining segments as necessary - // cur_seg_iter points to segment before incoming segment - while(cur_seg_iter != mSegments.end()) - { - cur_segmentp = *cur_seg_iter; - if (cur_segmentp == segment_to_insert) - { - ++cur_seg_iter; - continue; - } - - if (cur_segmentp->getStart() >= segment_to_insert->getStart()) - { - if(cur_segmentp->getEnd() <= segment_to_insert->getEnd()) - { - cur_segmentp->unlinkFromDocument(this); - // grab copy of iterator to erase, and bump it - segment_set_t::iterator seg_to_erase(cur_seg_iter++); - mSegments.erase(seg_to_erase); - continue; - } - else - { - // last overlapping segment, clip to end of incoming segment - // and stop traversal - cur_segmentp->setStart(segment_to_insert->getEnd()); - break; - } - } - ++cur_seg_iter; - } - } - - // layout potentially changed - needsReflow(reflow_start_index); -} - -//virtual -bool LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // handle triple click - if (!mTripleClickTimer.hasExpired()) - { - if (mSkipTripleClick) - { - return true; - } - - S32 real_line = getLineNumFromDocIndex(mCursorPos, false); - S32 line_start = -1; - S32 line_end = -1; - for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end(); - it != end_it; - ++it) - { - if (it->mLineNum < real_line) - { - continue; - } - if (it->mLineNum > real_line) - { - break; - } - if (line_start == -1) - { - line_start = it->mDocIndexStart; - } - line_end = it->mDocIndexEnd; - line_end = llclamp(line_end, 0, getLength()); - } - - if (line_start == -1) - { - return true; - } - - mSelectionEnd = line_start; - mSelectionStart = line_end; - setCursorPos(line_start); - - return true; - } - - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleMouseDown(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleMouseDown(x, y, mask); -} - -//virtual -bool LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (hasMouseCapture() && cur_segment && cur_segment->handleMouseUp(x, y, mask)) - { - // Did we just click on a link? - if (mURLClickSignal - && cur_segment->getStyle() - && cur_segment->getStyle()->isLink()) - { - // *TODO: send URL here? - (*mURLClickSignal)(this, LLSD() ); - } - return true; - } - - return LLUICtrl::handleMouseUp(x, y, mask); -} - -//virtual -bool LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleMiddleMouseDown(x, y, mask); -} - -//virtual -bool LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleMiddleMouseUp(x, y, mask); -} - -//virtual -bool LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleRightMouseDown(x, y, mask); -} - -//virtual -bool LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleRightMouseUp(x, y, mask); -} - -//virtual -bool LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - //Don't start triple click timer if user have clicked on scrollbar - mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); - if (x >= mVisibleTextRect.mLeft && x <= mVisibleTextRect.mRight - && y >= mVisibleTextRect.mBottom && y <= mVisibleTextRect.mTop) - { - mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); - } - - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleDoubleClick(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleDoubleClick(x, y, mask); -} - -//virtual -bool LLTextBase::handleHover(S32 x, S32 y, MASK mask) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleHover(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleHover(x, y, mask); -} - -//virtual -bool LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks)) - { - return true; - } - - return LLUICtrl::handleScrollWheel(x, y, clicks); -} - -//virtual -bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask) -{ - LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->handleToolTip(x, y, mask)) - { - return true; - } - - return LLUICtrl::handleToolTip(x, y, mask); -} - -//virtual -void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent) -{ - if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape) - { - bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; - - LLUICtrl::reshape( width, height, called_from_parent ); - - if (mScroller && scrolled_to_bottom && mTrackEnd) - { - // keep bottom of text buffer visible - // do this here as well as in reflow to handle case - // where shrinking from top, which causes buffer to temporarily - // not be scrolled to the bottom, since the scroll index - // specified the _top_ of the visible document region - mScroller->goToBottom(); - } - - // do this first after reshape, because other things depend on - // up-to-date mVisibleTextRect - updateRects(); - - needsReflow(); - } -} - -//virtual -void LLTextBase::draw() -{ - // reflow if needed, on demand - reflow(); - - // then update scroll position, as cursor may have moved - if (!mReadOnly) - { - updateScrollFromCursor(); - } - - LLRect text_rect; - if (mScroller) - { - mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &text_rect, this); - } - else - { - LLRect visible_lines_rect; - std::pair line_range = getVisibleLines(mClipPartial); - for (S32 i = line_range.first; i < line_range.second; i++) - { - if (visible_lines_rect.isEmpty()) - { - visible_lines_rect = mLineInfoList[i].mRect; - } - else - { - visible_lines_rect.unionWith(mLineInfoList[i].mRect); - } - } - text_rect = visible_lines_rect; - text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); - } - - if (mBGVisible) - { - F32 alpha = getCurrentTransparency(); - // clip background rect against extents, if we support scrolling - LLRect bg_rect = mVisibleTextRect; - if (mScroller) - { - bg_rect.intersectWith(text_rect); - } - LLColor4 bg_color = mReadOnly - ? mReadOnlyBgColor.get() - : hasFocus() - ? mFocusBgColor.get() - : mWriteableBgColor.get(); - gl_rect_2d(text_rect, bg_color % alpha, true); - } - - // Draw highlighted if needed - if( ll::ui::SearchableControl::getHighlighted() ) - { - LLColor4 bg_color = ll::ui::SearchableControl::getHighlightColor(); - LLRect bg_rect = mVisibleTextRect; - if( mScroller ) - bg_rect.intersectWith( text_rect ); - - gl_rect_2d( text_rect, bg_color, true ); - } - - bool should_clip = mClip || mScroller != NULL; - { LLLocalClipRect clip(text_rect, should_clip); - - // draw document view - if (mScroller) - { - drawChild(mScroller); - } - else - { - drawChild(mDocumentView); - } - - drawSelectionBackground(); - drawText(); - drawCursor(); - } - - mDocumentView->setVisibleDirect(false); - LLUICtrl::draw(); - mDocumentView->setVisibleDirect(true); -} - - -//virtual -void LLTextBase::setColor( const LLColor4& c ) -{ - mFgColor = c; - mStyleDirty = true; -} - -//virtual -void LLTextBase::setReadOnlyColor(const LLColor4 &c) -{ - mReadOnlyFgColor = c; - mStyleDirty = true; -} - -//virtual -void LLTextBase::onVisibilityChange( bool new_visibility ) -{ - LLContextMenu* menu = static_cast(mPopupMenuHandle.get()); - if(!new_visibility && menu) - { - menu->hide(); - } - LLUICtrl::onVisibilityChange(new_visibility); -} - -//virtual -void LLTextBase::setValue(const LLSD& value ) -{ - setText(value.asString()); -} - -//virtual -bool LLTextBase::canDeselect() const -{ - return hasSelection(); -} - - -//virtual -void LLTextBase::deselect() -{ - mSelectionStart = 0; - mSelectionEnd = 0; - mIsSelecting = false; -} - -bool LLTextBase::getSpellCheck() const -{ - return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck); -} - -const std::string& LLTextBase::getSuggestion(U32 index) const -{ - return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null; -} - -U32 LLTextBase::getSuggestionCount() const -{ - return mSuggestionList.size(); -} - -void LLTextBase::replaceWithSuggestion(U32 index) -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) - { - deselect(); - // Insert the suggestion in its place - LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]); - insertStringNoUndo(it->first, utf8str_to_wstring(mSuggestionList[index])); - - // Delete the misspelled word - removeStringNoUndo(it->first + (S32)suggestion.length(), it->second - it->first); - - - setCursorPos(it->first + (S32)suggestion.length()); - onSpellCheckPerformed(); - - break; - } - } - mSpellCheckStart = mSpellCheckEnd = -1; -} - -void LLTextBase::addToDictionary() -{ - if (canAddToDictionary()) - { - LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos)); - } -} - -bool LLTextBase::canAddToDictionary() const -{ - return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); -} - -void LLTextBase::addToIgnore() -{ - if (canAddToIgnore()) - { - LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos)); - } -} - -bool LLTextBase::canAddToIgnore() const -{ - return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); -} - -std::string LLTextBase::getMisspelledWord(U32 pos) const -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= pos) && (it->second >= pos) ) - { - return wstring_to_utf8str(getWText().substr(it->first, it->second - it->first)); - } - } - return LLStringUtil::null; -} - -bool LLTextBase::isMisspelledWord(U32 pos) const -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= pos) && (it->second >= pos) ) - { - return true; - } - } - return false; -} - -void LLTextBase::onSpellCheckSettingsChange() -{ - // Recheck the spelling on every change - mMisspellRanges.clear(); - mSpellCheckStart = mSpellCheckEnd = -1; -} - -void LLTextBase::onFocusReceived() -{ - LLUICtrl::onFocusReceived(); - if (!getLength() && !mLabel.empty()) - { - // delete label which is LLLabelTextSegment - clearSegments(); - } -} - -void LLTextBase::onFocusLost() -{ - LLUICtrl::onFocusLost(); - if (!getLength() && !mLabel.empty()) - { - resetLabel(); - } -} - -// Sets the scrollbar from the cursor position -void LLTextBase::updateScrollFromCursor() -{ - // Update scroll position even in read-only mode (when there's no cursor displayed) - // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736. - - if (!mScrollNeeded || !mScroller) - { - return; - } - mScrollNeeded = false; - - // scroll so that the cursor is at the top of the page - LLRect scroller_doc_window = getVisibleDocumentRect(); - LLRect cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); - mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5)); -} - -S32 LLTextBase::getLeftOffset(S32 width) -{ - switch (mHAlign) - { - case LLFontGL::LEFT: - return mHPad; - case LLFontGL::HCENTER: - return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2); - case LLFontGL::RIGHT: - { - // Font's rendering rounds string size, if value gets rounded - // down last symbol might not have enough space to render, - // compensate by adding an extra pixel as padding - const S32 right_padding = 1; - return llmax(mHPad, mVisibleTextRect.getWidth() - width - right_padding); - } - default: - return mHPad; - } -} - -void LLTextBase::reflow() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - - updateSegments(); - - if (mReflowIndex == S32_MAX) - { - return; - } - - bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - bool follow_selection = getLocalRect().overlaps(cursor_rect); // cursor is (potentially) visible - - // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing - cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop; - cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom; - - S32 first_line = getFirstVisibleLine(); - - // if scroll anchor not on first line, update it to first character of first line - if ((first_line < mLineInfoList.size()) - && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart - || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd)) - { - mScrollIndex = mLineInfoList[first_line].mDocIndexStart; - } - LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex); - // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing - first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop; - first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom; - - S32 reflow_count = 0; - while(mReflowIndex < S32_MAX) - { - // we can get into an infinite loop if the document height does not monotonically increase - // with decreasing width (embedded ui elements with alternate layouts). In that case, - // we want to stop reflowing after 2 iterations. We use 2, since we need to handle the case - // of introducing a vertical scrollbar causing a reflow with less width. We should also always - // use an even number of iterations to avoid user visible oscillation of the layout - if(++reflow_count > 2) - { - LL_DEBUGS() << "Breaking out of reflow due to possible infinite loop in " << getName() << LL_ENDL; - break; - } - - S32 start_index = mReflowIndex; - mReflowIndex = S32_MAX; - - // shrink document to minimum size (visible portion of text widget) - // to force inlined widgets with follows set to shrink - if (mWordWrap) - { - mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight()); - } - - S32 cur_top = 0; - - segment_set_t::iterator seg_iter = mSegments.begin(); - S32 seg_offset = 0; - S32 line_start_index = 0; - const F32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin - F32 remaining_pixels = text_available_width; - S32 line_count = 0; - - // find and erase line info structs starting at start_index and going to end of document - if (!mLineInfoList.empty()) - { - // find first element whose end comes after start_index - line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare()); - if (iter != mLineInfoList.end()) - { - line_start_index = iter->mDocIndexStart; - line_count = iter->mLineNum; - cur_top = iter->mRect.mTop; - getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); - mLineInfoList.erase(iter, mLineInfoList.end()); - } - } - - S32 line_height = 0; - S32 seg_line_offset = line_count + 1; - - while(seg_iter != mSegments.end()) - { - LLTextSegmentPtr segment = *seg_iter; - - // track maximum height of any segment on this line - S32 cur_index = segment->getStart() + seg_offset; - - // ask segment how many character fit in remaining space - S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, ll_round(remaining_pixels)) : S32_MAX, - seg_offset, - cur_index - line_start_index, - S32_MAX, - line_count - seg_line_offset); - - F32 segment_width; - S32 segment_height; - bool force_newline = segment->getDimensionsF32(seg_offset, character_count, segment_width, segment_height); - // grow line height as necessary based on reported height of this segment - line_height = llmax(line_height, segment_height); - remaining_pixels -= segment_width; - - seg_offset += character_count; - - S32 last_segment_char_on_line = segment->getStart() + seg_offset; - - // Note: make sure text will fit in width - use ceil, but also make sure - // ceil is used only once per line - S32 text_actual_width = llceil(text_available_width - remaining_pixels); - S32 text_left = getLeftOffset(text_actual_width); - LLRect line_rect(text_left, - cur_top, - text_left + text_actual_width, - cur_top - line_height); - - // if we didn't finish the current segment... - if (last_segment_char_on_line < segment->getEnd()) - { - // add line info and keep going - mLineInfoList.push_back(line_info( - line_start_index, - last_segment_char_on_line, - line_rect, - line_count)); - - line_start_index = segment->getStart() + seg_offset; - cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; - remaining_pixels = text_available_width; - line_height = 0; - } - // ...just consumed last segment.. - else if (++segment_set_t::iterator(seg_iter) == mSegments.end()) - { - mLineInfoList.push_back(line_info( - line_start_index, - last_segment_char_on_line, - line_rect, - line_count)); - cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; - break; - } - // ...or finished a segment and there are segments remaining on this line - else - { - // subtract pixels used and increment segment - if (force_newline) - { - mLineInfoList.push_back(line_info( - line_start_index, - last_segment_char_on_line, - line_rect, - line_count)); - line_start_index = segment->getStart() + seg_offset; - cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; - line_height = 0; - remaining_pixels = text_available_width; - } - ++seg_iter; - seg_offset = 0; - seg_line_offset = force_newline ? line_count + 1 : line_count; - } - if (force_newline) - { - line_count++; - } - } - - // calculate visible region for diplaying text - updateRects(); - - for (segment_set_t::iterator segment_it = mSegments.begin(); - segment_it != mSegments.end(); - ++segment_it) - { - LLTextSegmentPtr segmentp = *segment_it; - segmentp->updateLayout(*this); - - } - } - - // apply scroll constraints after reflowing text - if (!hasMouseCapture() && mScroller) - { - if (scrolled_to_bottom && mTrackEnd) - { - // keep bottom of text buffer visible - endOfDoc(); - } - else if (hasSelection() && follow_selection) - { - // keep cursor in same vertical position on screen when selecting text - LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); - LLRect old_cursor_rect = cursor_rect; - old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop; - old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom; - - mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); - } - else - { - // keep first line of text visible - LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex); - - // pass in desired rect in the coordinate frame of the document viewport - LLRect old_first_char_rect = first_char_rect; - old_first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop; - old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom; - - mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect); - } - } - - // reset desired x cursor position - updateCursorXPos(); -} - -LLRect LLTextBase::getTextBoundingRect() -{ - reflow(); - return mTextBoundingRect; -} - - -void LLTextBase::clearSegments() -{ - mSegments.clear(); - createDefaultSegment(); -} - -S32 LLTextBase::getLineStart( S32 line ) const -{ - S32 num_lines = getLineCount(); - if (num_lines == 0) - { - return 0; - } - - line = llclamp(line, 0, num_lines-1); - return mLineInfoList[line].mDocIndexStart; -} - -S32 LLTextBase::getLineEnd( S32 line ) const -{ - S32 num_lines = getLineCount(); - if (num_lines == 0) - { - return 0; - } - - line = llclamp(line, 0, num_lines-1); - return mLineInfoList[line].mDocIndexEnd; -} - - - -S32 LLTextBase::getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap) const -{ - if (mLineInfoList.empty()) - { - return 0; - } - else - { - line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_index, line_end_compare()); - if (include_wordwrap) - { - return iter - mLineInfoList.begin(); - } - else - { - if (iter == mLineInfoList.end()) - { - return mLineInfoList.back().mLineNum; - } - else - { - return iter->mLineNum; - } - } - } -} - -// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line. -S32 LLTextBase::getLineOffsetFromDocIndex( S32 startpos, bool include_wordwrap) const -{ - if (mLineInfoList.empty()) - { - return startpos; - } - else - { - line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare()); - return startpos - iter->mDocIndexStart; - } -} - -S32 LLTextBase::getFirstVisibleLine() const -{ - LLRect visible_region = getVisibleDocumentRect(); - - // binary search for line that starts before top of visible buffer - line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); - - return iter - mLineInfoList.begin(); -} - -std::pair LLTextBase::getVisibleLines(bool require_fully_visible) -{ - LLRect visible_region = getVisibleDocumentRect(); - line_list_t::const_iterator first_iter; - line_list_t::const_iterator last_iter; - - // make sure we have an up-to-date mLineInfoList - reflow(); - - if (require_fully_visible) - { - first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_top()); - last_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom()); - } - else - { - first_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); - last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_top()); - } - return std::pair(first_iter - mLineInfoList.begin(), last_iter - mLineInfoList.begin()); -} - - - -LLTextViewModel* LLTextBase::getViewModel() const -{ - return (LLTextViewModel*)mViewModel.get(); -} - -void LLTextBase::addDocumentChild(LLView* view) -{ - mDocumentView->addChild(view); -} - -void LLTextBase::removeDocumentChild(LLView* view) -{ - mDocumentView->removeChild(view); -} - - -void LLTextBase::updateSegments() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - createDefaultSegment(); -} - -void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const -{ - *seg_iter = getSegIterContaining(startpos); - if (*seg_iter == mSegments.end()) - { - *offsetp = 0; - } - else - { - *offsetp = startpos - (**seg_iter)->getStart(); - } -} - -void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ) -{ - *seg_iter = getSegIterContaining(startpos); - if (*seg_iter == mSegments.end()) - { - *offsetp = 0; - } - else - { - *offsetp = startpos - (**seg_iter)->getStart(); - } -} - -LLTextBase::segment_set_t::iterator LLTextBase::getEditableSegIterContaining(S32 index) -{ - segment_set_t::iterator it = getSegIterContaining(index); - segment_set_t::iterator orig_it = it; - - if (it == mSegments.end()) return it; - - if (!(*it)->canEdit() - && index == (*it)->getStart() - && it != mSegments.begin()) - { - it--; - if ((*it)->canEdit()) - { - return it; - } - } - return orig_it; -} - -LLTextBase::segment_set_t::const_iterator LLTextBase::getEditableSegIterContaining(S32 index) const -{ - segment_set_t::const_iterator it = getSegIterContaining(index); - segment_set_t::const_iterator orig_it = it; - if (it == mSegments.end()) return it; - - if (!(*it)->canEdit() - && index == (*it)->getStart() - && it != mSegments.begin()) - { - it--; - if ((*it)->canEdit()) - { - return it; - } - } - return orig_it; -} - -LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index) -{ - static LLPointer index_segment = new LLIndexSegment(); - - // when there are no segments, we return the end iterator, which must be checked by caller - if (mSegments.size() <= 1) { return mSegments.begin(); } - - index_segment->setStart(index); - index_segment->setEnd(index); - segment_set_t::iterator it = mSegments.upper_bound(index_segment); - return it; -} - -LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const -{ - static LLPointer index_segment = new LLIndexSegment(); - - // when there are no segments, we return the end iterator, which must be checked by caller - if (mSegments.size() <= 1) { return mSegments.begin(); } - - index_segment->setStart(index); - index_segment->setEnd(index); - LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(index_segment); - return it; -} - -// Finds the text segment (if any) at the give local screen position -LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line) -{ - // Find the cursor position at the requested local screen position - S32 offset = getDocIndexFromLocalCoord( x, y, false, hit_past_end_of_line); - segment_set_t::iterator seg_iter = getSegIterContaining(offset); - if (seg_iter != mSegments.end()) - { - return *seg_iter; - } - else - { - return LLTextSegmentPtr(); - } -} - -void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) -{ - // work out the XUI menu file to use for this url - LLUrlMatch match; - std::string url = in_url; - if (! LLUrlRegistry::instance().findUrl(url, match)) - { - return; - } - - std::string xui_file = match.getMenuName(); - if (xui_file.empty()) - { - return; - } - - // set up the callbacks for all of the potential menu items, N.B. we - // don't use const ref strings in callbacks in case url goes out of scope - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url)); - registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url)); - registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url)); - registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url, true)); - registrar.add("Url.Block", boost::bind(&LLUrlAction::blockObject, url)); - registrar.add("Url.Unblock", boost::bind(&LLUrlAction::unblockObject, url)); - registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url)); - registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, url)); - registrar.add("Url.AddFriend", boost::bind(&LLUrlAction::addFriend, url)); - registrar.add("Url.RemoveFriend", boost::bind(&LLUrlAction::removeFriend, url)); - registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url)); - registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url)); - registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url)); - registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); - registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url)); - - // create and return the context menu from the XUI file - - LLContextMenu* menu = static_cast(mPopupMenuHandle.get()); - if (menu) - { - menu->die(); - mPopupMenuHandle.markDead(); - } - llassert(LLMenuGL::sMenuContainer != NULL); - menu = LLUICtrlFactory::getInstance()->createFromFile(xui_file, LLMenuGL::sMenuContainer, - LLMenuHolderGL::child_registry_t::instance()); - if (menu) - { - mPopupMenuHandle = menu->getHandle(); - - if (mIsFriendSignal) - { - bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url))); - LLView* addFriendButton = menu->getChild("add_friend"); - LLView* removeFriendButton = menu->getChild("remove_friend"); - - if (addFriendButton && removeFriendButton) - { - addFriendButton->setEnabled(!isFriend); - removeFriendButton->setEnabled(isFriend); - } - } - - if (mIsObjectBlockedSignal) - { - bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url)); - LLView* blockButton = menu->getChild("block_object"); - LLView* unblockButton = menu->getChild("unblock_object"); - - if (blockButton && unblockButton) - { - blockButton->setVisible(!is_blocked); - unblockButton->setVisible(is_blocked); - } - } - menu->show(x, y); - LLMenuGL::showPopup(this, menu, x, y); - } -} - -void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params) -{ - // clear out the existing text and segments - getViewModel()->setDisplay(LLWStringUtil::null); - - clearSegments(); -// createDefaultSegment(); - - deselect(); - - // append the new text (supports Url linking) - std::string text(utf8str); - LLStringUtil::removeCRLF(text); - - // appendText modifies mCursorPos... - appendText(text, false, input_params); - // ...so move cursor to top after appending text - if (!mTrackEnd) - { - startOfDoc(); - } - - onValueChange(0, getLength()); -} - -// virtual -const std::string& LLTextBase::getText() const -{ - return getViewModel()->getStringValue(); -} - -// IDEVO - icons can be UI image names or UUID sent from -// server with avatar display name -static LLUIImagePtr image_from_icon_name(const std::string& icon_name) -{ - if (LLUUID::validate(icon_name)) - { - return LLUI::getUIImageByID( LLUUID(icon_name) ); - } - else - { - return LLUI::getUIImage(icon_name); - } -} - - -void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - LLStyle::Params style_params(input_params); - style_params.fillFrom(getStyleParams()); - - S32 part = (S32)LLTextParser::WHOLE; - if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). - { - S32 start=0,end=0; - LLUrlMatch match; - std::string text = new_text; - while (LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3), isContentTrusted() || mAlwaysShowIcons)) - { - start = match.getStart(); - end = match.getEnd()+1; - - LLStyle::Params link_params(style_params); - link_params.overwriteFrom(match.getStyle()); - - // output the text before the Url - if (start > 0) - { - if (part == (S32)LLTextParser::WHOLE || - part == (S32)LLTextParser::START) - { - part = (S32)LLTextParser::START; - } - else - { - part = (S32)LLTextParser::MIDDLE; - } - std::string subtext=text.substr(0,start); - appendAndHighlightText(subtext, part, style_params); - } - - // add icon before url if need - LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted() || mAlwaysShowIcons); - if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() ) - { - setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon")); - } - - // output the styled Url - appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly()); - bool tooltip_required = !match.getTooltip().empty(); - - // set the tooltip for the Url label - if (tooltip_required) - { - setLastSegmentToolTip(match.getTooltip()); - } - - // show query part of url with gray color only for LLUrlEntryHTTP url entries - std::string label = match.getQuery(); - if (label.size()) - { - link_params.color = LLColor4::grey; - link_params.readonly_color = LLColor4::grey; - appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly()); - - // set the tooltip for the query part of url - if (tooltip_required) - { - setLastSegmentToolTip(match.getTooltip()); - } - } - - // move on to the rest of the text after the Url - if (end < (S32)text.length()) - { - text = text.substr(end,text.length() - end); - end=0; - part=(S32)LLTextParser::END; - } - else - { - break; - } - } - if (part != (S32)LLTextParser::WHOLE) - part=(S32)LLTextParser::END; - if (end < (S32)text.length()) - appendAndHighlightText(text, part, style_params); - } - else - { - appendAndHighlightText(new_text, part, style_params); - } -} - -void LLTextBase::setLastSegmentToolTip(const std::string &tooltip) -{ - segment_set_t::iterator it = getSegIterContaining(getLength()-1); - if (it != mSegments.end()) - { - LLTextSegmentPtr segment = *it; - segment->setToolTip(tooltip); - } -} - -void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - if (new_text.empty()) - return; - - if(prepend_newline) - appendLineBreakSegment(input_params); - appendTextImpl(new_text,input_params); -} - -void LLTextBase::setLabel(const LLStringExplicit& label) -{ - mLabel = label; - resetLabel(); -} - -bool LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text ) -{ - mLabel.setArg(key, text); - return true; -} - -void LLTextBase::resetLabel() -{ - if (useLabel()) - { - clearSegments(); - - LLStyle* style = new LLStyle(getStyleParams()); - style->setColor(mTentativeFgColor); - LLStyleConstSP sp(style); - - LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, mLabel.getWString().length() + 1, *this); - insertSegment(label); - } -} - -bool LLTextBase::useLabel() const -{ - return !getLength() && !mLabel.empty() && !hasFocus(); -} - -void LLTextBase::setFont(const LLFontGL* font) -{ - mFont = font; - mStyleDirty = true; -} - -void LLTextBase::needsReflow(S32 index) -{ - LL_DEBUGS() << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << LL_ENDL; - mReflowIndex = llmin(mReflowIndex, index); -} - -S32 LLTextBase::removeFirstLine() -{ - if (!mLineInfoList.empty()) - { - S32 length = getLineEnd(0); - deselect(); - removeStringNoUndo(0, length); - return length; - } - return 0; -} - -void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params) -{ - segment_vec_t segments; - LLStyleConstSP sp(new LLStyle(style_params)); - segments.push_back(new LLLineBreakTextSegment(sp, getLength())); - - insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments); -} - -void LLTextBase::appendImageSegment(const LLStyle::Params& style_params) -{ - if(getPlainText()) - { - return; - } - segment_vec_t segments; - LLStyleConstSP sp(new LLStyle(style_params)); - segments.push_back(new LLImageTextSegment(sp, getLength(),*this)); - - insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments); -} - -void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo) -{ - segment_vec_t segments; - LLWString widget_wide_text = utf8str_to_wstring(text); - segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + widget_wide_text.size())); - - insertStringNoUndo(getLength(), widget_wide_text, &segments); -} - -void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only) -{ - // Save old state - S32 selection_start = mSelectionStart; - S32 selection_end = mSelectionEnd; - bool was_selecting = mIsSelecting; - S32 cursor_pos = mCursorPos; - S32 old_length = getLength(); - bool cursor_was_at_end = (mCursorPos == old_length); - - deselect(); - - setCursorPos(old_length); - - if (mParseHighlights) - { - LLStyle::Params highlight_params(style_params); - - LLSD pieces = LLTextParser::instance().parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part); - for (S32 i = 0; i < pieces.size(); i++) - { - LLSD color_llsd = pieces[i]["color"]; - LLColor4 lcolor; - lcolor.setValue(color_llsd); - highlight_params.color = lcolor; - - LLWString wide_text; - wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); - - S32 cur_length = getLength(); - LLStyleConstSP sp(new LLStyle(highlight_params)); - LLTextSegmentPtr segmentp; - if (underline_on_hover_only || mSkipLinkUnderline) - { - highlight_params.font.style("NORMAL"); - LLStyleConstSP normal_sp(new LLStyle(highlight_params)); - segmentp = new LLOnHoverChangeableTextSegment(sp, normal_sp, cur_length, cur_length + wide_text.size(), *this); - } - else - { - segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this); - } - segment_vec_t segments; - segments.push_back(segmentp); - insertStringNoUndo(cur_length, wide_text, &segments); - } - } - else - { - LLWString wide_text; - wide_text = utf8str_to_wstring(new_text); - - segment_vec_t segments; - S32 segment_start = old_length; - S32 segment_end = old_length + wide_text.size(); - LLStyleConstSP sp(new LLStyle(style_params)); - if (underline_on_hover_only || mSkipLinkUnderline) - { - LLStyle::Params normal_style_params(style_params); - normal_style_params.font.style("NORMAL"); - LLStyleConstSP normal_sp(new LLStyle(normal_style_params)); - segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this)); - } - else - { - segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this)); - } - - insertStringNoUndo(getLength(), wide_text, &segments); - } - - // Set the cursor and scroll position - if (selection_start != selection_end) - { - mSelectionStart = selection_start; - mSelectionEnd = selection_end; - - mIsSelecting = was_selecting; - setCursorPos(cursor_pos); - } - else if (cursor_was_at_end) - { - setCursorPos(getLength()); - } - else - { - setCursorPos(cursor_pos); - } -} - -void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only) -{ - if (new_text.empty()) - { - return; - } - - std::string::size_type start = 0; - std::string::size_type pos = new_text.find("\n", start); - - while (pos != std::string::npos) - { - if (pos != start) - { - std::string str = std::string(new_text,start,pos-start); - appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only); - } - appendLineBreakSegment(style_params); - start = pos+1; - pos = new_text.find("\n", start); - } - - std::string str = std::string(new_text, start, new_text.length() - start); - appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only); -} - - -void LLTextBase::replaceUrl(const std::string &url, - const std::string &label, - const std::string &icon) -{ - // get the full (wide) text for the editor so we can change it - LLWString text = getWText(); - LLWString wlabel = utf8str_to_wstring(label); - bool modified = false; - S32 seg_start = 0; - - // iterate through each segment looking for ones styled as links - segment_set_t::iterator it; - for (it = mSegments.begin(); it != mSegments.end(); ++it) - { - LLTextSegment *seg = *it; - LLStyleConstSP style = seg->getStyle(); - - // update segment start/end length in case we replaced text earlier - S32 seg_length = seg->getEnd() - seg->getStart(); - seg->setStart(seg_start); - seg->setEnd(seg_start + seg_length); - - // if we find a link with our Url, then replace the label - if (style->getLinkHREF() == url) - { - S32 start = seg->getStart(); - S32 end = seg->getEnd(); - text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1); - seg->setEnd(start + wlabel.size()); - modified = true; - } - - // Icon might be updated when more avatar or group info - // becomes available - if (style->isImage() && style->getLinkHREF() == url) - { - LLUIImagePtr image = image_from_icon_name( icon ); - if (image) - { - LLStyle::Params icon_params; - icon_params.image = image; - LLStyleConstSP new_style(new LLStyle(icon_params)); - seg->setStyle(new_style); - modified = true; - } - } - - // work out the character offset for the next segment - seg_start = seg->getEnd(); - } - - // update the editor with the new (wide) text string - if (modified) - { - getViewModel()->setDisplay(text); - deselect(); - setCursorPos(mCursorPos); - needsReflow(); - } -} - - -void LLTextBase::setWText(const LLWString& text) -{ - setText(wstring_to_utf8str(text)); -} - -const LLWString& LLTextBase::getWText() const -{ - return getViewModel()->getDisplay(); -} - -// If round is true, if the position is on the right half of a character, the cursor -// will be put to its right. If round is false, the cursor will always be put to the -// character's left. - -S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line) const -{ - // Figure out which line we're nearest to. - LLRect doc_rect = mDocumentView->getRect(); - S32 doc_y = local_y - doc_rect.mBottom; - - // binary search for line that starts before local_y - line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_y, compare_bottom()); - - if (!mLineInfoList.size() || line_iter == mLineInfoList.end()) - { - return getLength(); // past the end - } - - S32 pos = getLength(); - F32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft; - - segment_set_t::iterator line_seg_iter; - S32 line_seg_offset; - for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); - line_seg_iter != mSegments.end(); - ++line_seg_iter, line_seg_offset = 0) - { - const LLTextSegmentPtr segmentp = *line_seg_iter; - - S32 segment_line_start = segmentp->getStart() + line_seg_offset; - S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start; - F32 text_width; - S32 text_height; - bool newline = segmentp->getDimensionsF32(line_seg_offset, segment_line_length, text_width, text_height); - - if(newline) - { - pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); - break; - } - - // if we've reached a line of text *below* the mouse cursor, doc index is first character on that line - if (hit_past_end_of_line && doc_y > line_iter->mRect.mTop) - { - pos = segment_line_start; - break; - } - if (local_x < start_x + text_width) // cursor to left of right edge of text - { - // Figure out which character we're nearest to. - S32 offset; - if (!segmentp->canEdit()) - { - F32 segment_width; - S32 segment_height; - segmentp->getDimensionsF32(0, segmentp->getEnd() - segmentp->getStart(), segment_width, segment_height); - if (round && local_x - start_x > segment_width / 2) - { - offset = segment_line_length; - } - else - { - offset = 0; - } - } - else - { - offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); - } - pos = segment_line_start + offset; - break; - } - else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd) - { - if (getLineNumFromDocIndex(line_iter->mDocIndexEnd - 1) == line_iter->mLineNum) - { - // if segment wraps to the next line we should step one char back - // to compensate for the space char between words - // which is removed due to wrapping - pos = llclamp(line_iter->mDocIndexEnd - 1, 0, getLength()); - } - else - { - pos = llclamp(line_iter->mDocIndexEnd, 0, getLength()); - } - break; - } - start_x += text_width; - } - - return pos; -} - -// returns rectangle of insertion caret -// in document coordinate frame from given index into text -LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const -{ - if (mLineInfoList.empty()) - { - return LLRect(); - } - - // clamp pos to valid values - pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); - - line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare()); - - segment_set_t::iterator line_seg_iter; - S32 line_seg_offset; - segment_set_t::iterator cursor_seg_iter; - S32 cursor_seg_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); - getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset); - - F32 doc_left_precise = line_iter->mRect.mLeft; - - while(line_seg_iter != mSegments.end()) - { - const LLTextSegmentPtr segmentp = *line_seg_iter; - - if (line_seg_iter == cursor_seg_iter) - { - // cursor advanced to right based on difference in offset of cursor to start of line - F32 segment_width; - S32 segment_height; - segmentp->getDimensionsF32(line_seg_offset, cursor_seg_offset - line_seg_offset, segment_width, segment_height); - doc_left_precise += segment_width; - - break; - } - else - { - // add remainder of current text segment to cursor position - F32 segment_width; - S32 segment_height; - segmentp->getDimensionsF32(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset, segment_width, segment_height); - doc_left_precise += segment_width; - // offset will be 0 for all segments after the first - line_seg_offset = 0; - // go to next text segment on this line - ++line_seg_iter; - } - } - - LLRect doc_rect; - doc_rect.mLeft = doc_left_precise; - doc_rect.mBottom = line_iter->mRect.mBottom; - doc_rect.mTop = line_iter->mRect.mTop; - - // set rect to 0 width - doc_rect.mRight = doc_rect.mLeft; - - return doc_rect; -} - -LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const -{ - LLRect content_window_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); - if (mBorderVisible) - { - content_window_rect.stretch(-1); - } - - LLRect local_rect; - - if (mLineInfoList.empty()) - { - // return default height rect in upper left - local_rect = content_window_rect; - local_rect.mBottom = local_rect.mTop - mFont->getLineHeight(); - return local_rect; - } - - // get the rect in document coordinates - LLRect doc_rect = getDocRectFromDocIndex(pos); - - // compensate for scrolled, inset view of doc - LLRect scrolled_view_rect = getVisibleDocumentRect(); - local_rect = doc_rect; - local_rect.translate(content_window_rect.mLeft - scrolled_view_rect.mLeft, - content_window_rect.mBottom - scrolled_view_rect.mBottom); - - return local_rect; -} - -void LLTextBase::updateCursorXPos() -{ - // reset desired x cursor position - mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft; -} - - -void LLTextBase::startOfLine() -{ - S32 offset = getLineOffsetFromDocIndex(mCursorPos); - setCursorPos(mCursorPos - offset); -} - -void LLTextBase::endOfLine() -{ - S32 line = getLineNumFromDocIndex(mCursorPos); - S32 num_lines = getLineCount(); - if (line + 1 >= num_lines) - { - setCursorPos(getLength()); - } - else - { - setCursorPos( getLineStart(line + 1) - 1 ); - } -} - -void LLTextBase::startOfDoc() -{ - setCursorPos(0); - if (mScroller) - { - mScroller->goToTop(); - } -} - -void LLTextBase::endOfDoc() -{ - setCursorPos(getLength()); - if (mScroller) - { - mScroller->goToBottom(); - } -} - -void LLTextBase::changePage( S32 delta ) -{ - const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10; - if (delta == 0 || !mScroller) return; - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - - if( delta == -1 ) - { - mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE); - } - else - if( delta == 1 ) - { - mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE); - } - - if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect) - { - // cursor didn't change apparent position, so move to top or bottom of document, respectively - if (delta < 0) - { - startOfDoc(); - } - else - { - endOfDoc(); - } - } - else - { - setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false); - } -} - -// Picks a new cursor position based on the screen size of text being drawn. -void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset ) -{ - setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset); -} - - -void LLTextBase::changeLine( S32 delta ) -{ - S32 line = getLineNumFromDocIndex(mCursorPos); - S32 max_line_nb = getLineCount() - 1; - max_line_nb = (max_line_nb < 0 ? 0 : max_line_nb); - - S32 new_line = llclamp(line + delta, 0, max_line_nb); - - if (new_line != line) - { - LLRect visible_region = getVisibleDocumentRect(); - S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, - mLineInfoList[new_line].mRect.mBottom + mVisibleTextRect.mBottom - visible_region.mBottom, true); - S32 actual_line = getLineNumFromDocIndex(new_cursor_pos); - if (actual_line != new_line) - { - // line edge, correcting position by 1 to move onto proper line - new_cursor_pos += new_line - actual_line; - } - setCursorPos(new_cursor_pos, true); - } -} - -bool LLTextBase::scrolledToStart() -{ - return mScroller->isAtTop(); -} - -bool LLTextBase::scrolledToEnd() -{ - return mScroller->isAtBottom(); -} - -bool LLTextBase::setCursor(S32 row, S32 column) -{ - if (row < 0 || column < 0) return false; - - S32 n_lines = mLineInfoList.size(); - for (S32 line = row; line < n_lines; ++line) - { - const line_info& li = mLineInfoList[line]; - - if (li.mLineNum < row) - { - continue; - } - else if (li.mLineNum > row) - { - break; // invalid column specified - } - - // Found the given row. - S32 line_length = li.mDocIndexEnd - li.mDocIndexStart;; - if (column >= line_length) - { - column -= line_length; - continue; - } - - // Found the given column. - updateCursorXPos(); - S32 doc_pos = li.mDocIndexStart + column; - return setCursorPos(doc_pos); - } - - return false; // invalid row or column specified -} - - -bool LLTextBase::setCursorPos(S32 cursor_pos, bool keep_cursor_offset) -{ - S32 new_cursor_pos = cursor_pos; - if (new_cursor_pos != mCursorPos) - { - new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos); - } - - mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength()); - needsScroll(); - if (!keep_cursor_offset) - updateCursorXPos(); - // did we get requested position? - return new_cursor_pos == cursor_pos; -} - -// constraint cursor to editable segments of document -S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction) -{ - segment_set_t::iterator segment_iter; - S32 offset; - getSegmentAndOffset(index, &segment_iter, &offset); - if (segment_iter == mSegments.end()) - { - return 0; - } - - LLTextSegmentPtr segmentp = *segment_iter; - - if (segmentp->canEdit()) - { - return segmentp->getStart() + offset; - } - else if (segmentp->getStart() < index && index < segmentp->getEnd()) - { - // bias towards document end - if (increasing_direction) - { - return segmentp->getEnd(); - } - // bias towards document start - else - { - return segmentp->getStart(); - } - } - else - { - return index; - } -} - -void LLTextBase::updateRects() -{ - LLRect old_text_rect = mVisibleTextRect; - mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); - - if (mLineInfoList.empty()) - { - mTextBoundingRect = LLRect(0, mVPad, mHPad, 0); - } - else - { - mTextBoundingRect = mLineInfoList.begin()->mRect; - for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin(); - line_iter != mLineInfoList.end(); - ++line_iter) - { - mTextBoundingRect.unionWith(line_iter->mRect); - } - - mTextBoundingRect.mTop += mVPad; - - S32 delta_pos = 0; - - switch(mVAlign) - { - case LLFontGL::TOP: - delta_pos = llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom); - break; - case LLFontGL::VCENTER: - delta_pos = (llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom) + (mVisibleTextRect.mBottom - mTextBoundingRect.mBottom)) / 2; - break; - case LLFontGL::BOTTOM: - delta_pos = mVisibleTextRect.mBottom - mTextBoundingRect.mBottom; - break; - case LLFontGL::BASELINE: - // do nothing - break; - } - // move line segments to fit new document rect - for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) - { - it->mRect.translate(0, delta_pos); - } - mTextBoundingRect.translate(0, delta_pos); - } - - // update document container dimensions according to text contents - LLRect doc_rect; - // use old mVisibleTextRect constraint document to width of viewable region - doc_rect.mBottom = llmin(mVisibleTextRect.mBottom, mTextBoundingRect.mBottom); - doc_rect.mLeft = 0; - - // allow horizontal scrolling? - // if so, use entire width of text contents - // otherwise, stop at width of mVisibleTextRect - //FIXME: consider use of getWordWrap() instead - doc_rect.mRight = mScroller - ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight) - : mVisibleTextRect.getWidth(); - doc_rect.mTop = llmax(mVisibleTextRect.mTop, mTextBoundingRect.mTop); - - if (!mScroller) - { - // push doc rect to top of text widget - switch(mVAlign) - { - case LLFontGL::TOP: - doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop); - break; - case LLFontGL::VCENTER: - doc_rect.translate(0, (mVisibleTextRect.getHeight() - doc_rect.mTop) / 2); - case LLFontGL::BOTTOM: - default: - break; - } - } - - mDocumentView->setShape(doc_rect); - - //update mVisibleTextRect *after* mDocumentView has been resized - // so that scrollbars are added if document needs to scroll - // since mVisibleTextRect does not include scrollbars - mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); - //FIXME: replace border with image? - if (mBorderVisible) - { - mVisibleTextRect.stretch(-1); - } - if (mVisibleTextRect != old_text_rect) - { - needsReflow(); - } - - // update mTextBoundingRect after mVisibleTextRect took scrolls into account - if (!mLineInfoList.empty() && mScroller) - { - S32 delta_pos = 0; - - switch(mVAlign) - { - case LLFontGL::TOP: - delta_pos = llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom); - break; - case LLFontGL::VCENTER: - delta_pos = (llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom) + (mVisibleTextRect.mBottom - mTextBoundingRect.mBottom)) / 2; - break; - case LLFontGL::BOTTOM: - delta_pos = mVisibleTextRect.mBottom - mTextBoundingRect.mBottom; - break; - case LLFontGL::BASELINE: - // do nothing - break; - } - // move line segments to fit new visible rect - if (delta_pos != 0) - { - for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) - { - it->mRect.translate(0, delta_pos); - } - mTextBoundingRect.translate(0, delta_pos); - } - } - - // update document container again, using new mVisibleTextRect (that has scrollbars enabled as needed) - doc_rect.mBottom = llmin(mVisibleTextRect.mBottom, mTextBoundingRect.mBottom); - doc_rect.mLeft = 0; - doc_rect.mRight = mScroller - ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight) - : mVisibleTextRect.getWidth(); - doc_rect.mTop = llmax(mVisibleTextRect.getHeight(), mTextBoundingRect.getHeight()) + doc_rect.mBottom; - if (!mScroller) - { - // push doc rect to top of text widget - switch(mVAlign) - { - case LLFontGL::TOP: - doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop); - break; - case LLFontGL::VCENTER: - doc_rect.translate(0, (mVisibleTextRect.getHeight() - doc_rect.mTop) / 2); - case LLFontGL::BOTTOM: - default: - break; - } - } - mDocumentView->setShape(doc_rect); -} - - -void LLTextBase::startSelection() -{ - if( !mIsSelecting ) - { - mIsSelecting = true; - mSelectionStart = mCursorPos; - mSelectionEnd = mCursorPos; - } -} - -void LLTextBase::endSelection() -{ - if( mIsSelecting ) - { - mIsSelecting = false; - mSelectionEnd = mCursorPos; - } -} - -// get portion of document that is visible in text editor -LLRect LLTextBase::getVisibleDocumentRect() const -{ - if (mScroller) - { - return mScroller->getVisibleContentRect(); - } - else if (mClip) - { - LLRect visible_text_rect = getVisibleTextRect(); - LLRect doc_rect = mDocumentView->getRect(); - visible_text_rect.translate(-doc_rect.mLeft, -doc_rect.mBottom); - - // reject partially visible lines - LLRect visible_lines_rect; - for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end(); - it != end_it; - ++it) - { - bool line_visible = mClipPartial ? visible_text_rect.contains(it->mRect) : visible_text_rect.overlaps(it->mRect); - if (line_visible) - { - if (visible_lines_rect.isEmpty()) - { - visible_lines_rect = it->mRect; - } - else - { - visible_lines_rect.unionWith(it->mRect); - } - } - } - return visible_lines_rect; - } - else - { // entire document rect is visible - // but offset according to height of widget - - LLRect doc_rect = mDocumentView->getLocalRect(); - doc_rect.mLeft -= mDocumentView->getRect().mLeft; - // adjust for height of text above widget baseline - doc_rect.mBottom = doc_rect.getHeight() - mVisibleTextRect.getHeight(); - return doc_rect; - } -} - -boost::signals2::connection LLTextBase::setURLClickedCallback(const commit_signal_t::slot_type& cb) -{ - if (!mURLClickSignal) - { - mURLClickSignal = new commit_signal_t(); - } - return mURLClickSignal->connect(cb); -} - -boost::signals2::connection LLTextBase::setIsFriendCallback(const is_friend_signal_t::slot_type& cb) -{ - if (!mIsFriendSignal) - { - mIsFriendSignal = new is_friend_signal_t(); - } - return mIsFriendSignal->connect(cb); -} - -boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb) -{ - if (!mIsObjectBlockedSignal) - { - mIsObjectBlockedSignal = new is_blocked_signal_t(); - } - return mIsObjectBlockedSignal->connect(cb); -} - -// -// LLTextSegment -// - -LLTextSegment::~LLTextSegment() -{} - -bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = 0; return false; } -bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const -{ - F32 fwidth = 0; - bool result = getDimensionsF32(first_char, num_chars, fwidth, height); - width = ll_round(fwidth); - return result; -} - -S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; } -S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const { return 0; } -void LLTextSegment::updateLayout(const LLTextBase& editor) {} -F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; } -bool LLTextSegment::canEdit() const { return false; } -void LLTextSegment::unlinkFromDocument(LLTextBase*) {} -void LLTextSegment::linkToDocument(LLTextBase*) {} -const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; } -//void LLTextSegment::setColor(const LLColor4 &color) {} -LLStyleConstSP LLTextSegment::getStyle() const {static LLStyleConstSP sp(new LLStyle()); return sp; } -void LLTextSegment::setStyle(LLStyleConstSP style) {} -void LLTextSegment::setToken( LLKeywordToken* token ) {} -LLKeywordToken* LLTextSegment::getToken() const { return NULL; } -void LLTextSegment::setToolTip( const std::string &msg ) {} -void LLTextSegment::dump() const {} -bool LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return false; } -bool LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return false; } -bool LLTextSegment::handleScrollHWheel(S32 x, S32 y, S32 clicks) { return false; } -bool LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return false; } -const std::string& LLTextSegment::getName() const -{ - return LLStringUtil::null; -} -void LLTextSegment::onMouseCaptureLost() {} -void LLTextSegment::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {} -void LLTextSegment::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {} -bool LLTextSegment::hasMouseCapture() { return false; } - -// -// LLNormalTextSegment -// - -LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ) -: LLTextSegment(start, end), - mStyle( style ), - mToken(NULL), - mEditor(editor) -{ - mFontHeight = mStyle->getFont()->getLineHeight(); - - LLUIImagePtr image = mStyle->getImage(); - if (image.notNull()) - { - mImageLoadedConnection = image->addLoadedCallback(boost::bind(&LLTextBase::needsReflow, &mEditor, start)); - } -} - -LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) -: LLTextSegment(start, end), - mToken(NULL), - mEditor(editor) -{ - mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color)); - - mFontHeight = mStyle->getFont()->getLineHeight(); -} - -LLNormalTextSegment::~LLNormalTextSegment() -{ - mImageLoadedConnection.disconnect(); -} - - -F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) -{ - if( end - start > 0 ) - { - return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect); - } - return draw_rect.mLeft; -} - -// Draws a single text segment, reversing the color for selection if needed. -F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect) -{ - F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; - - const LLWString &text = getWText(); - - F32 right_x = rect.mLeft; - if (!mStyle->isVisible()) - { - return right_x; - } - - const LLFontGL* font = mStyle->getFont(); - - LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha; - - if( selection_start > seg_start ) - { - // Draw normally - S32 start = seg_start; - S32 end = llmin( selection_start, seg_end ); - S32 length = end - start; - font->render(text, start, - rect, - color, - LLFontGL::LEFT, mEditor.mTextVAlign, - LLFontGL::NORMAL, - mStyle->getShadowType(), - length, - &right_x, - mEditor.getUseEllipses(), - mEditor.getUseColor()); - } - rect.mLeft = right_x; - - if( (selection_start < seg_end) && (selection_end > seg_start) ) - { - // Draw reversed - S32 start = llmax( selection_start, seg_start ); - S32 end = llmin( selection_end, seg_end ); - S32 length = end - start; - - font->render(text, start, - rect, - mStyle->getSelectedColor().get(), - LLFontGL::LEFT, mEditor.mTextVAlign, - LLFontGL::NORMAL, - LLFontGL::NO_SHADOW, - length, - &right_x, - mEditor.getUseEllipses(), - mEditor.getUseColor()); - } - rect.mLeft = right_x; - if( selection_end < seg_end ) - { - // Draw normally - S32 start = llmax( selection_end, seg_start ); - S32 end = seg_end; - S32 length = end - start; - font->render(text, start, - rect, - color, - LLFontGL::LEFT, mEditor.mTextVAlign, - LLFontGL::NORMAL, - mStyle->getShadowType(), - length, - &right_x, - mEditor.getUseEllipses(), - mEditor.getUseColor()); - } - return right_x; -} - -bool LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask) -{ - if (getStyle() && getStyle()->isLink()) - { - // Only process the click if it's actually in this segment, not to the right of the end-of-line. - if(mEditor.getSegmentAtLocalPos(x, y, false) == this) - { - LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND); - return true; - } - } - return false; -} - -bool LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - if (getStyle() && getStyle()->isLink()) - { - // Only process the click if it's actually in this segment, not to the right of the end-of-line. - if(mEditor.getSegmentAtLocalPos(x, y, false) == this) - { - mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF()); - return true; - } - } - return false; -} - -bool LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (getStyle() && getStyle()->isLink()) - { - // Only process the click if it's actually in this segment, not to the right of the end-of-line. - if(mEditor.getSegmentAtLocalPos(x, y, false) == this) - { - // eat mouse down event on hyperlinks, so we get the mouse up - return true; - } - } - - return false; -} - -bool LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if (getStyle() && getStyle()->isLink()) - { - // Only process the click if it's actually in this segment, not to the right of the end-of-line. - if(mEditor.getSegmentAtLocalPos(x, y, false) == this) - { - std::string url = getStyle()->getLinkHREF(); - if (!mEditor.mForceUrlsExternal) - { - LLUrlAction::clickAction(url, mEditor.isContentTrusted()); - } - else if (!LLUrlAction::executeSLURL(url, mEditor.isContentTrusted())) - { - LLUrlAction::openURLExternal(url); - } - return true; - } - } - - return false; -} - -bool LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask) -{ - std::string msg; - // do we have a tooltip for a loaded keyword (for script editor)? - if (mToken && !mToken->getToolTip().empty()) - { - const LLWString& wmsg = mToken->getToolTip(); - LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg), (mToken->getType() == LLKeywordToken::TT_FUNCTION)); - return true; - } - // or do we have an explicitly set tooltip (e.g., for Urls) - if (!mTooltip.empty()) - { - LLToolTipMgr::instance().show(mTooltip); - return true; - } - - return false; -} - -void LLNormalTextSegment::setToolTip(const std::string& tooltip) -{ - // we cannot replace a keyword tooltip that's loaded from a file - if (mToken) - { - LL_WARNS() << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << LL_ENDL; - return; - } - mTooltip = tooltip; -} - -bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const -{ - height = 0; - width = 0; - if (num_chars > 0) - { - height = mFontHeight; - const LLWString &text = getWText(); - // if last character is a newline, then return true, forcing line break - width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars, true); - } - return false; -} - -S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const -{ - const LLWString &text = getWText(); - return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset, - (F32)segment_local_x_coord, - F32_MAX, - num_chars, - round); -} - -S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const -{ - const LLWString &text = getWText(); - - LLUIImagePtr image = mStyle->getImage(); - if( image.notNull()) - { - num_pixels = llmax(0, num_pixels - image->getWidth()); - } - - S32 last_char = mEnd; - - // set max characters to length of segment, or to first newline - max_chars = llmin(max_chars, last_char - (mStart + segment_offset)); - - // if no character yet displayed on this line, don't require word wrapping since - // we can just move to the next line, otherwise insist on it so we make forward progress - LLFontGL::EWordWrapStyle word_wrap_style = (line_offset == 0) - ? LLFontGL::WORD_BOUNDARY_IF_POSSIBLE - : LLFontGL::ONLY_WORD_BOUNDARIES; - - - S32 offsetLength = text.length() - (segment_offset + mStart); - - if(getLength() < segment_offset + mStart) - { - LL_INFOS() << "getLength() < segment_offset + mStart\t getLength()\t" << getLength() << "\tsegment_offset:\t" - << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << "\tmax_chars\t" << max_chars << LL_ENDL; - } - - if( (offsetLength + 1) < max_chars) - { - LL_INFOS() << "offsetString.length() + 1 < max_chars\t max_chars:\t" << max_chars << "\toffsetString.length():\t" << offsetLength << " getLength() : " - << getLength() << "\tsegment_offset:\t" << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << LL_ENDL; - } - - S32 num_chars = mStyle->getFont()->maxDrawableChars( text.c_str() + (segment_offset + mStart), - (F32)num_pixels, - max_chars, - word_wrap_style); - - if (num_chars == 0 - && line_offset == 0 - && max_chars > 0) - { - // If at the beginning of a line, and a single character won't fit, draw it anyway - num_chars = 1; - } - - // include *either* the EOF or newline character in this run of text - // but not both - S32 last_char_in_run = mStart + segment_offset + num_chars; - // check length first to avoid indexing off end of string - if (last_char_in_run < mEnd - && (last_char_in_run >= getLength())) - { - num_chars++; - } - return num_chars; -} - -void LLNormalTextSegment::dump() const -{ - LL_INFOS() << "Segment [" << -// mColor.mV[VX] << ", " << -// mColor.mV[VY] << ", " << -// mColor.mV[VZ] << "]\t[" << - mStart << ", " << - getEnd() << "]" << - LL_ENDL; -} - -/*virtual*/ -const LLWString& LLNormalTextSegment::getWText() const -{ - return mEditor.getWText(); -} - -/*virtual*/ -const S32 LLNormalTextSegment::getLength() const -{ - return mEditor.getLength(); -} - -LLLabelTextSegment::LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ) -: LLNormalTextSegment(style, start, end, editor) -{ -} - -LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) -: LLNormalTextSegment(color, start, end, editor, is_visible) -{ -} - -/*virtual*/ -const LLWString& LLLabelTextSegment::getWText() const -{ - return mEditor.getWlabel(); -} -/*virtual*/ -const S32 LLLabelTextSegment::getLength() const -{ - return mEditor.getWlabel().length(); -} - -// -// LLEmojiTextSegment -// -LLEmojiTextSegment::LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor) - : LLNormalTextSegment(style, start, end, editor) -{ -} - -LLEmojiTextSegment::LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) - : LLNormalTextSegment(color, start, end, editor, is_visible) -{ -} - -bool LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask) -{ - if (mTooltip.empty()) - { - LLWString emoji = getWText().substr(getStart(), getEnd() - getStart()); - if (!emoji.empty()) - { - mTooltip = LLEmojiHelper::instance().getToolTip(emoji[0]); - } - } - - return LLNormalTextSegment::handleToolTip(x, y, mask); -} - -// -// LLOnHoverChangeableTextSegment -// - -LLOnHoverChangeableTextSegment::LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ): - LLNormalTextSegment(normal_style, start, end, editor), - mHoveredStyle(style), - mNormalStyle(normal_style){} - -/*virtual*/ -F32 LLOnHoverChangeableTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) -{ - F32 result = LLNormalTextSegment::draw(start, end, selection_start, selection_end, draw_rect); - if (end == mEnd - mStart) - { - mStyle = mNormalStyle; - } - return result; -} - -/*virtual*/ -bool LLOnHoverChangeableTextSegment::handleHover(S32 x, S32 y, MASK mask) -{ - mStyle = mEditor.getSkipLinkUnderline() ? mNormalStyle : mHoveredStyle; - return LLNormalTextSegment::handleHover(x, y, mask); -} - - -// -// LLInlineViewSegment -// - -LLInlineViewSegment::LLInlineViewSegment(const Params& p, S32 start, S32 end) -: LLTextSegment(start, end), - mView(p.view), - mForceNewLine(p.force_newline), - mLeftPad(p.left_pad), - mRightPad(p.right_pad), - mTopPad(p.top_pad), - mBottomPad(p.bottom_pad) -{ -} - -LLInlineViewSegment::~LLInlineViewSegment() -{ - mView->die(); -} - -bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const -{ - if (first_char == 0 && num_chars == 0) - { - // We didn't fit on a line or were forced to new string - // the widget will fall on the next line, so width here is 0 - width = 0; - - if (mForceNewLine) - { - // Chat, string can't be smaller then font height even if it is empty - LLStyleSP s(new LLStyle(LLStyle::Params().visible(true))); - height = s->getFont()->getLineHeight(); - - return true; // new line - } - else - { - // height from previous segment in same string will be used, word-wrap - height = 0; - } - - } - else - { - width = mLeftPad + mRightPad + mView->getRect().getWidth(); - height = mBottomPad + mTopPad + mView->getRect().getHeight(); - } - - return false; -} - -S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const -{ - // if putting a widget anywhere but at the beginning of a line - // and the widget doesn't fit or mForceNewLine is true - // then return 0 chars for that line, and all characters for the next - if (mForceNewLine && line_ind == 0) - { - return 0; - } - else if (line_offset != 0 && num_pixels < mView->getRect().getWidth()) - { - return 0; - } - else - { - return mEnd - mStart; - } -} - -void LLInlineViewSegment::updateLayout(const LLTextBase& editor) -{ - LLRect start_rect = editor.getDocRectFromDocIndex(mStart); - mView->setOrigin(start_rect.mLeft + mLeftPad, start_rect.mBottom + mBottomPad); -} - -F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) -{ - // return padded width of widget - // widget is actually drawn during mDocumentView's draw() - return (F32)(draw_rect.mLeft + mView->getRect().getWidth() + mLeftPad + mRightPad); -} - -void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor) -{ - editor->removeDocumentChild(mView); -} - -void LLInlineViewSegment::linkToDocument(LLTextBase* editor) -{ - editor->addDocumentChild(mView); -} - -LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1) -{ - LLStyleSP s( new LLStyle(LLStyle::Params().visible(true))); - - mFontHeight = s->getFont()->getLineHeight(); -} -LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1) -{ - mFontHeight = style->getFont()->getLineHeight(); -} -LLLineBreakTextSegment::~LLLineBreakTextSegment() -{ -} -bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const -{ - width = 0; - height = mFontHeight; - - return true; -} -S32 LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const -{ - return 1; -} -F32 LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) -{ - return draw_rect.mLeft; -} - -LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor) -: LLTextSegment(pos,pos+1), - mStyle( style ), - mEditor(editor) -{ -} - -LLImageTextSegment::~LLImageTextSegment() -{ -} - -static const S32 IMAGE_HPAD = 3; - -bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const -{ - width = 0; - height = mStyle->getFont()->getLineHeight(); - - LLUIImagePtr image = mStyle->getImage(); - if( num_chars>0 && image.notNull()) - { - width += image->getWidth() + IMAGE_HPAD; - height = llmax(height, image->getHeight() + IMAGE_HPAD ); - } - return false; -} - -S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const -{ - LLUIImagePtr image = mStyle->getImage(); - - if (image.isNull()) - { - return 1; - } - - S32 image_width = image->getWidth(); - if(line_offset == 0 || num_pixels>image_width + IMAGE_HPAD) - { - return 1; - } - - return 0; -} - -bool LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask) -{ - if (!mTooltip.empty()) - { - LLToolTipMgr::instance().show(mTooltip); - return true; - } - - return false; -} - -void LLImageTextSegment::setToolTip(const std::string& tooltip) -{ - mTooltip = tooltip; -} - -F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) -{ - if ( (start >= 0) && (end <= mEnd - mStart)) - { - LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha; - LLUIImagePtr image = mStyle->getImage(); - if (image.notNull()) - { - S32 style_image_height = image->getHeight(); - S32 style_image_width = image->getWidth(); - // Text is drawn from the top of the draw_rect downward - - S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2); - // Align image to center of draw rect - S32 image_bottom = text_center - (style_image_height / 2); - image->draw(draw_rect.mLeft, image_bottom, - style_image_width, style_image_height, color); - - const S32 IMAGE_HPAD = 3; - return draw_rect.mLeft + style_image_width + IMAGE_HPAD; - } - } - return 0.0; -} - -void LLTextBase::setWordWrap(bool wrap) -{ - mWordWrap = wrap; -} +/** + * @file lltextbase.cpp + * @author Martin Reddy + * @brief The base class of text box/editor, providing Url handling support + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2009-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 "lltextbase.h" + +#include "llemojidictionary.h" +#include "llemojihelper.h" +#include "lllocalcliprect.h" +#include "llmenugl.h" +#include "llscrollcontainer.h" +#include "llspellcheck.h" +#include "llstl.h" +#include "lltextparser.h" +#include "lltextutil.h" +#include "lltooltip.h" +#include "lltrans.h" +#include "lluictrl.h" +#include "llurlaction.h" +#include "llurlregistry.h" +#include "llview.h" +#include "llwindow.h" +#include + +const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds +const S32 CURSOR_THICKNESS = 2; +const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. + +LLTextBase::line_info::line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num) +: mDocIndexStart(index_start), + mDocIndexEnd(index_end), + mRect(rect), + mLineNum(line_num) +{} + +bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const +{ + // sort empty spans (e.g. 11-11) after previous non-empty spans (e.g. 5-11) + if (a->getEnd() == b->getEnd()) + { + return a->getStart() < b->getStart(); + } + else + { + return a->getEnd() < b->getEnd(); + } +} + + +// helper functors +bool LLTextBase::compare_bottom::operator()(const S32& a, const LLTextBase::line_info& b) const +{ + return a > b.mRect.mBottom; // bottom of a is higher than bottom of b +} + +bool LLTextBase::compare_bottom::operator()(const LLTextBase::line_info& a, const S32& b) const +{ + return a.mRect.mBottom > b; // bottom of a is higher than bottom of b +} + +bool LLTextBase::compare_bottom::operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const +{ + return a.mRect.mBottom > b.mRect.mBottom; // bottom of a is higher than bottom of b +} + +// helper functors +bool LLTextBase::compare_top::operator()(const S32& a, const LLTextBase::line_info& b) const +{ + return a > b.mRect.mTop; // top of a is higher than top of b +} + +bool LLTextBase::compare_top::operator()(const LLTextBase::line_info& a, const S32& b) const +{ + return a.mRect.mTop > b; // top of a is higher than top of b +} + +bool LLTextBase::compare_top::operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const +{ + return a.mRect.mTop > b.mRect.mTop; // top of a is higher than top of b +} + +struct LLTextBase::line_end_compare +{ + bool operator()(const S32& pos, const LLTextBase::line_info& info) const + { + return (pos < info.mDocIndexEnd); + } + + bool operator()(const LLTextBase::line_info& info, const S32& pos) const + { + return (info.mDocIndexEnd < pos); + } + + bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const + { + return (a.mDocIndexEnd < b.mDocIndexEnd); + } + +}; + +////////////////////////////////////////////////////////////////////////// +// +// LLTextBase +// + +// register LLTextBase::Params under name "textbase" +static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase"); + +LLTextBase::LineSpacingParams::LineSpacingParams() +: multiple("multiple", 1.f), + pixels("pixels", 0) +{ +} + + +LLTextBase::Params::Params() +: cursor_color("cursor_color"), + text_color("text_color"), + text_readonly_color("text_readonly_color"), + text_tentative_color("text_tentative_color"), + bg_visible("bg_visible", false), + border_visible("border_visible", false), + bg_readonly_color("bg_readonly_color"), + bg_writeable_color("bg_writeable_color"), + bg_focus_color("bg_focus_color"), + text_selected_color("text_selected_color"), + bg_selected_color("bg_selected_color"), + allow_scroll("allow_scroll", true), + plain_text("plain_text",false), + track_end("track_end", false), + read_only("read_only", false), + skip_link_underline("skip_link_underline", false), + spellcheck("spellcheck", false), + v_pad("v_pad", 0), + h_pad("h_pad", 0), + clip("clip", true), + clip_partial("clip_partial", true), + line_spacing("line_spacing"), + max_text_length("max_length", 255), + font_shadow("font_shadow"), + text_valign("text_valign"), + wrap("wrap"), + trusted_content("trusted_content", true), + always_show_icons("always_show_icons", false), + use_ellipses("use_ellipses", false), + use_emoji("use_emoji", true), + use_color("use_color", true), + parse_urls("parse_urls", false), + force_urls_external("force_urls_external", false), + parse_highlights("parse_highlights", false) +{ + addSynonym(track_end, "track_bottom"); + addSynonym(wrap, "word_wrap"); + addSynonym(parse_urls, "allow_html"); +} + + +LLTextBase::LLTextBase(const LLTextBase::Params &p) +: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), + mURLClickSignal(NULL), + mIsFriendSignal(NULL), + mIsObjectBlockedSignal(NULL), + mMaxTextByteLength( p.max_text_length ), + mFont(p.font), + mFontShadow(p.font_shadow), + mPopupMenuHandle(), + mReadOnly(p.read_only), + mSkipTripleClick(false), + mSkipLinkUnderline(p.skip_link_underline), + mSpellCheck(p.spellcheck), + mSpellCheckStart(-1), + mSpellCheckEnd(-1), + mCursorColor(p.cursor_color), + mFgColor(p.text_color), + mBorderVisible( p.border_visible ), + mReadOnlyFgColor(p.text_readonly_color), + mTentativeFgColor(p.text_tentative_color()), + mWriteableBgColor(p.bg_writeable_color), + mReadOnlyBgColor(p.bg_readonly_color), + mFocusBgColor(p.bg_focus_color), + mTextSelectedColor(p.text_selected_color), + mSelectedBGColor(p.bg_selected_color), + mReflowIndex(S32_MAX), + mCursorPos( 0 ), + mScrollNeeded(false), + mDesiredXPixel(-1), + mHPad(p.h_pad), + mVPad(p.v_pad), + mHAlign(p.font_halign), + mVAlign(p.font_valign), + mTextVAlign(p.text_valign.isProvided() ? p.text_valign.getValue() : p.font_valign.getValue()), + mLineSpacingMult(p.line_spacing.multiple), + mLineSpacingPixels(p.line_spacing.pixels), + mClip(p.clip), + mClipPartial(p.clip_partial && !p.allow_scroll), + mTrustedContent(p.trusted_content), + mAlwaysShowIcons(p.always_show_icons), + mTrackEnd( p.track_end ), + mScrollIndex(-1), + mSelectionStart( 0 ), + mSelectionEnd( 0 ), + mIsSelecting( false ), + mPlainText ( p.plain_text ), + mWordWrap(p.wrap), + mUseEllipses( p.use_ellipses ), + mUseEmoji(p.use_emoji), + mUseColor(p.use_color), + mParseHTML(p.parse_urls), + mForceUrlsExternal(p.force_urls_external), + mParseHighlights(p.parse_highlights), + mBGVisible(p.bg_visible), + mScroller(NULL), + mStyleDirty(true) +{ + if(p.allow_scroll) + { + LLScrollContainer::Params scroll_params; + scroll_params.name = "text scroller"; + scroll_params.rect = getLocalRect(); + scroll_params.follows.flags = FOLLOWS_ALL; + scroll_params.is_opaque = false; + scroll_params.mouse_opaque = false; + scroll_params.min_auto_scroll_rate = 200; + scroll_params.max_auto_scroll_rate = 800; + scroll_params.border_visible = p.border_visible; + mScroller = LLUICtrlFactory::create(scroll_params); + addChild(mScroller); + } + + LLView::Params view_params; + view_params.name = "text_contents"; + view_params.rect = LLRect(0, 500, 500, 0); + view_params.mouse_opaque = false; + + mDocumentView = LLUICtrlFactory::create(view_params); + if (mScroller) + { + mScroller->addChild(mDocumentView); + } + else + { + addChild(mDocumentView); + } + + if (mSpellCheck) + { + LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLTextBase::onSpellCheckSettingsChange, this)); + } + mSpellCheckTimer.reset(); + + createDefaultSegment(); + + updateRects(); +} + +LLTextBase::~LLTextBase() +{ + mSegments.clear(); + LLContextMenu* menu = static_cast(mPopupMenuHandle.get()); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } + delete mURLClickSignal; + delete mIsFriendSignal; + delete mIsObjectBlockedSignal; +} + +void LLTextBase::initFromParams(const LLTextBase::Params& p) +{ + LLUICtrl::initFromParams(p); + resetDirty(); // Update saved text state + updateSegments(); + + // HACK: work around enabled == readonly design bug -- RN + // setEnabled will modify our read only status, so do this after + // LLTextBase::initFromParams + if (p.read_only.isProvided()) + { + mReadOnly = p.read_only; + } +} + +bool LLTextBase::truncate() +{ + bool did_truncate = false; + + // First rough check - if we're less than 1/4th the size, we're OK + if (getLength() >= S32(mMaxTextByteLength / 4)) + { + // Have to check actual byte size + S32 utf8_byte_size = 0; + LLSD value = getViewModel()->getValue(); + if (value.type() == LLSD::TypeString) + { + // save a copy for strings. + utf8_byte_size = value.size(); + } + else + { + // non string LLSDs need explicit conversion to string + utf8_byte_size = value.asString().size(); + } + + if ( utf8_byte_size > mMaxTextByteLength ) + { + // Truncate safely in UTF-8 + std::string temp_utf8_text = value.asString(); + temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); + LLWString text = utf8str_to_wstring( temp_utf8_text ); + // remove extra bit of current string, to preserve formatting, etc. + removeStringNoUndo(text.size(), getWText().size() - text.size()); + did_truncate = true; + } + } + + return did_truncate; +} + +const LLStyle::Params& LLTextBase::getStyleParams() +{ + //FIXME: convert mDefaultStyle to a flyweight http://www.boost.org/doc/libs/1_40_0/libs/flyweight/doc/index.html + //and eliminate color member values + if (mStyleDirty) + { + mStyle + .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor + .readonly_color(LLUIColor(&mReadOnlyFgColor)) + .selected_color(LLUIColor(&mTextSelectedColor)) + .font(mFont) + .drop_shadow(mFontShadow); + mStyleDirty = false; + } + return mStyle; +} + +void LLTextBase::beforeValueChange() +{ + +} + +void LLTextBase::onValueChange(S32 start, S32 end) +{ +} + +std::vector LLTextBase::getSelectionRects() +{ + // Nor supposed to be called without selection + llassert(hasSelection()); + llassert(!mLineInfoList.empty()); + + std::vector selection_rects; + + S32 selection_left = llmin(mSelectionStart, mSelectionEnd); + S32 selection_right = llmax(mSelectionStart, mSelectionEnd); + + // Skip through the lines we aren't drawing. + LLRect content_display_rect = getVisibleDocumentRect(); + + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); + line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); + + bool done = false; + + // Find the coordinates of the selected area + for (; line_iter != end_iter && !done; ++line_iter) + { + // is selection visible on this line? + if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) + { + segment_set_t::iterator segment_iter; + S32 segment_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); + + // Use F32 otherwise a string of multiple segments + // will accumulate a large error + F32 left_precise = line_iter->mRect.mLeft; + F32 right_precise = line_iter->mRect.mLeft; + + for (; segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) + { + LLTextSegmentPtr segmentp = *segment_iter; + + S32 segment_line_start = segmentp->getStart() + segment_offset; + S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); + + if (segment_line_start > segment_line_end) break; + + F32 segment_width = 0; + S32 segment_height = 0; + + // if selection after beginning of segment + if (selection_left >= segment_line_start) + { + S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; + segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); + left_precise += segment_width; + } + + // if selection_right == segment_line_end then that means we are the first character of the next segment + // or first character of the next line, in either case we want to add the length of the current segment + // to the selection rectangle and continue. + // if selection right > segment_line_end then selection spans end of current segment... + if (selection_right >= segment_line_end) + { + // extend selection slightly beyond end of line + // to indicate selection of newline character (use "n" character to determine width) + S32 num_chars = segment_line_end - segment_line_start; + segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); + right_precise += segment_width; + } + // else if selection ends on current segment... + else + { + S32 num_chars = selection_right - segment_line_start; + segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); + right_precise += segment_width; + + break; + } + } + + LLRect selection_rect; + selection_rect.mLeft = left_precise; + selection_rect.mRight = right_precise; + selection_rect.mBottom = line_iter->mRect.mBottom; + selection_rect.mTop = line_iter->mRect.mTop; + + selection_rects.push_back(selection_rect); + } + } + + return selection_rects; +} + +// Draws the black box behind the selected text +void LLTextBase::drawSelectionBackground() +{ + // Draw selection even if we don't have keyboard focus for search/replace + if (hasSelection() && !mLineInfoList.empty()) + { + std::vector selection_rects = getSelectionRects(); + + // Draw the selection box (we're using a box instead of reversing the colors on the selected text). + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + const LLColor4& color = mSelectedBGColor; + F32 alpha = hasFocus() ? 0.7f : 0.3f; + alpha *= getDrawContext().mAlpha; + + LLColor4 selection_color(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], alpha); + LLRect content_display_rect = getVisibleDocumentRect(); + + for (std::vector::iterator rect_it = selection_rects.begin(); + rect_it != selection_rects.end(); + ++rect_it) + { + LLRect selection_rect = *rect_it; + if (mScroller) + { + // If scroller is On content_display_rect has correct rect and safe to use as is + // Note: we might need to account for border + selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom); + } + else + { + // If scroller is Off content_display_rect will have rect from document, adjusted to text width, heigh and position + // and we have to acount for offset depending on position + S32 v_delta = 0; + S32 h_delta = 0; + switch (mVAlign) + { + case LLFontGL::TOP: + v_delta = mVisibleTextRect.mTop - content_display_rect.mTop - mVPad; + break; + case LLFontGL::VCENTER: + v_delta = (llmax(mVisibleTextRect.getHeight() - content_display_rect.mTop, -content_display_rect.mBottom) + (mVisibleTextRect.mBottom - content_display_rect.mBottom)) / 2; + break; + case LLFontGL::BOTTOM: + v_delta = mVisibleTextRect.mBottom - content_display_rect.mBottom; + break; + default: + break; + } + switch (mHAlign) + { + case LLFontGL::LEFT: + h_delta = mVisibleTextRect.mLeft - content_display_rect.mLeft + mHPad; + break; + case LLFontGL::HCENTER: + h_delta = (llmax(mVisibleTextRect.getWidth() - content_display_rect.mLeft, -content_display_rect.mRight) + (mVisibleTextRect.mRight - content_display_rect.mRight)) / 2; + break; + case LLFontGL::RIGHT: + h_delta = mVisibleTextRect.mRight - content_display_rect.mRight; + break; + default: + break; + } + selection_rect.translate(h_delta, v_delta); + } + gl_rect_2d(selection_rect, selection_color); + } + } +} + +void LLTextBase::drawCursor() +{ + F32 alpha = getDrawContext().mAlpha; + + if( hasFocus() + && gFocusMgr.getAppHasFocus() + && !mReadOnly) + { + const LLWString &wtext = getWText(); + const llwchar* text = wtext.c_str(); + + LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); + cursor_rect.translate(-1, 0); + segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos); + + // take style from last segment + LLTextSegmentPtr segmentp; + + if (seg_it != mSegments.end()) + { + segmentp = *seg_it; + } + else + { + return; + } + + // Draw the cursor + // (Flash the cursor every half second starting a fixed time after the last keystroke) + F32 elapsed = mCursorBlinkTimer.getElapsedTimeF32(); + if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) + { + + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) + { + S32 segment_width = 0; + S32 segment_height = 0; + segmentp->getDimensions(mCursorPos - segmentp->getStart(), 1, segment_width, segment_height); + S32 width = llmax(CURSOR_THICKNESS, segment_width); + cursor_rect.mRight = cursor_rect.mLeft + width; + } + else + { + cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS; + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLColor4 cursor_color = mCursorColor.get() % alpha; + gGL.color4fv( cursor_color.mV ); + + gl_rect_2d(cursor_rect); + + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') + { + LLColor4 text_color; + const LLFontGL* fontp; + text_color = segmentp->getColor(); + fontp = segmentp->getStyle()->getFont(); + fontp->render(text, mCursorPos, cursor_rect, + LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha), + LLFontGL::LEFT, mTextVAlign, + LLFontGL::NORMAL, + LLFontGL::NO_SHADOW, + 1); + } + + // Make sure the IME is in the right place + LLRect screen_pos = calcScreenRect(); + LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) ); + + ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]); + ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]); + getWindow()->setLanguageTextInput( ime_pos ); + } + } +} + +void LLTextBase::drawText() +{ + S32 text_len = getLength(); + + if (text_len <= 0 && mLabel.empty()) + { + return; + } + else if (useLabel()) + { + text_len = mLabel.getWString().length(); + } + + S32 selection_left = -1; + S32 selection_right = -1; + // Draw selection even if we don't have keyboard focus for search/replace + if( hasSelection()) + { + selection_left = llmin( mSelectionStart, mSelectionEnd ); + selection_right = llmax( mSelectionStart, mSelectionEnd ); + } + + std::pair line_range = getVisibleLines(mClipPartial); + S32 first_line = line_range.first; + S32 last_line = line_range.second; + if (first_line >= last_line) + { + return; + } + + S32 line_start = getLineStart(first_line); + // find first text segment that spans top of visible portion of text buffer + segment_set_t::iterator seg_iter = getSegIterContaining(line_start); + if (seg_iter == mSegments.end()) + { + return; + } + + // Perform spell check if needed + if ( (getSpellCheck()) && (getWText().length() > 2) ) + { + // Calculate start and end indices for the spell checking range + S32 start = line_start; + S32 end = getLineEnd(last_line); + + if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) ) + { + const LLWString& wstrText = getWText(); + mMisspellRanges.clear(); + + segment_set_t::const_iterator seg_it = getSegIterContaining(start); + while (mSegments.end() != seg_it) + { + LLTextSegmentPtr text_segment = *seg_it; + if ( (text_segment.isNull()) || (text_segment->getStart() >= end) ) + { + break; + } + + if (!text_segment->canEdit()) + { + ++seg_it; + continue; + } + + // Combine adjoining text segments into one + U32 seg_start = text_segment->getStart(), seg_end = llmin(text_segment->getEnd(), end); + while (mSegments.end() != ++seg_it) + { + text_segment = *seg_it; + if ( (text_segment.isNull()) || (!text_segment->canEdit()) || (text_segment->getStart() >= end) ) + { + break; + } + seg_end = llmin(text_segment->getEnd(), end); + } + + // Find the start of the first word + U32 word_start = seg_start, word_end = -1; + U32 text_length = wstrText.length(); + while ( (word_start < text_length) && (!LLStringOps::isAlpha(wstrText[word_start])) ) + { + word_start++; + } + + // Iterate over all words in the text block and check them one by one + while (word_start < seg_end) + { + // Find the end of the current word (special case handling for "'" when it's used as a contraction) + word_end = word_start + 1; + while ( (word_end < seg_end) && + ((LLWStringUtil::isPartOfWord(wstrText[word_end])) || + ((L'\'' == wstrText[word_end]) && + (LLStringOps::isAlnum(wstrText[word_end - 1])) && (LLStringOps::isAlnum(wstrText[word_end + 1])))) ) + { + word_end++; + } + if (word_end > seg_end) + { + break; + } + + if (word_start < text_length && word_end <= text_length && word_end > word_start) + { + std::string word = wstring_to_utf8str(wstrText.substr(word_start, word_end - word_start)); + + // Don't process words shorter than 3 characters + if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) + { + mMisspellRanges.push_back(std::pair(word_start, word_end)); + } + } + + // Find the start of the next word + word_start = word_end + 1; + while ( (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) ) + { + word_start++; + } + } + } + + mSpellCheckStart = start; + mSpellCheckEnd = end; + } + } + else + { + mMisspellRanges.clear(); + } + + LLTextSegmentPtr cur_segment = *seg_iter; + + std::list >::const_iterator misspell_it = std::lower_bound(mMisspellRanges.begin(), mMisspellRanges.end(), std::pair(line_start, 0)); + for (S32 cur_line = first_line; cur_line < last_line; cur_line++) + { + S32 next_line = cur_line + 1; + line_info& line = mLineInfoList[cur_line]; + + S32 next_start = -1; + S32 line_end = text_len; + + if (next_line < getLineCount()) + { + next_start = getLineStart(next_line); + line_end = next_start; + } + + LLRectf text_rect(line.mRect.mLeft, line.mRect.mTop, line.mRect.mRight, line.mRect.mBottom); + text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents + text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position + + // draw a single line of text + S32 seg_start = line_start; + while( seg_start < line_end ) + { + while( cur_segment->getEnd() <= seg_start ) + { + seg_iter++; + if (seg_iter == mSegments.end()) + { + LL_WARNS() << "Ran off the segmentation end!" << LL_ENDL; + + return; + } + cur_segment = *seg_iter; + } + + S32 seg_end = llmin(line_end, cur_segment->getEnd()); + S32 clipped_end = seg_end - cur_segment->getStart(); + + if (mUseEllipses // using ellipses + && clipped_end == line_end // last segment on line + && next_line == last_line // this is the last visible line + && last_line < (S32)mLineInfoList.size()) // and there is more text to display + { + // more lines of text to go, but we can't fit them + // so shrink text rect to force ellipses + text_rect.mRight -= 2; + } + + // Draw squiggly lines under any visible misspelled words + while ( (mMisspellRanges.end() != misspell_it) && (misspell_it->first < seg_end) && (misspell_it->second > seg_start) ) + { + // Skip the current word if the user is still busy editing it + if ( (!mSpellCheckTimer.hasExpired()) && (misspell_it->first <= (U32)mCursorPos) && (misspell_it->second >= (U32)mCursorPos) ) + { + ++misspell_it; + continue; + } + + U32 misspell_start = llmax(misspell_it->first, seg_start), misspell_end = llmin(misspell_it->second, seg_end); + S32 squiggle_start = 0, squiggle_end = 0, pony = 0; + cur_segment->getDimensions(seg_start - cur_segment->getStart(), misspell_start - seg_start, squiggle_start, pony); + cur_segment->getDimensions(misspell_start - cur_segment->getStart(), misspell_end - misspell_start, squiggle_end, pony); + squiggle_start += text_rect.mLeft; + + pony = (squiggle_end + 3) / 6; + squiggle_start += squiggle_end / 2 - pony * 3; + squiggle_end = squiggle_start + pony * 6; + + S32 squiggle_bottom = text_rect.mBottom + (S32)cur_segment->getStyle()->getFont()->getDescenderHeight(); + + gGL.color4ub(255, 0, 0, 200); + while (squiggle_start + 1 < squiggle_end) + { + gl_line_2d(squiggle_start, squiggle_bottom, squiggle_start + 2, squiggle_bottom - 2); + if (squiggle_start + 3 < squiggle_end) + { + gl_line_2d(squiggle_start + 2, squiggle_bottom - 3, squiggle_start + 4, squiggle_bottom - 1); + } + squiggle_start += 4; + } + + if (misspell_it->second > seg_end) + { + break; + } + ++misspell_it; + } + + text_rect.mLeft = cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect); + + seg_start = clipped_end + cur_segment->getStart(); + } + + line_start = next_start; + } +} + +/////////////////////////////////////////////////////////////////// +// Returns change in number of characters in mWText + +S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments ) +{ + beforeValueChange(); + + S32 old_len = getLength(); // length() returns character length + S32 insert_len = wstr.length(); + + pos = getEditableIndex(pos, true); + if (pos > old_len) + { + pos = old_len; + // Should not happen, + // if you encounter this, check where wrong position comes from + llassert(false); + } + + segment_set_t::iterator seg_iter = getEditableSegIterContaining(pos); + + LLTextSegmentPtr default_segment; + + LLTextSegmentPtr segmentp; + if (seg_iter != mSegments.end()) + { + segmentp = *seg_iter; + } + else + { + //segmentp = mSegments.back(); + return pos; + } + + if (segmentp->canEdit()) + { + segmentp->setEnd(segmentp->getEnd() + insert_len); + if (seg_iter != mSegments.end()) + { + ++seg_iter; + } + } + else + { + // create default editable segment to hold new text + LLStyleConstSP sp(new LLStyle(getStyleParams())); + default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this); + } + + // shift remaining segments to right + for(;seg_iter != mSegments.end(); ++seg_iter) + { + LLTextSegmentPtr segmentp = *seg_iter; + segmentp->setStart(segmentp->getStart() + insert_len); + segmentp->setEnd(segmentp->getEnd() + insert_len); + } + + // insert new segments + if (segments) + { + if (default_segment.notNull()) + { + // potentially overwritten by segments passed in + insertSegment(default_segment); + } + for (segment_vec_t::iterator seg_iter = segments->begin(); + seg_iter != segments->end(); + ++seg_iter) + { + LLTextSegment* segmentp = *seg_iter; + insertSegment(segmentp); + } + } + + // Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us) + if (mUseEmoji) + { + LLStyleSP emoji_style; + LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL; + for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++) + { + llwchar code = wstr[text_kitty]; + bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code); + if (isEmoji) + { + if (!emoji_style) + { + emoji_style = new LLStyle(getStyleParams()); + emoji_style->setFont(LLFontGL::getFontEmojiLarge()); + } + + S32 new_seg_start = pos + text_kitty; + insertSegment(new LLEmojiTextSegment(emoji_style, new_seg_start, new_seg_start + 1, *this)); + } + } + } + + getViewModel()->getEditableDisplay().insert(pos, wstr); + + if ( truncate() ) + { + insert_len = getLength() - old_len; + } + + onValueChange(pos, pos + insert_len); + needsReflow(pos); + + return insert_len; +} + +S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) +{ + + beforeValueChange(); + segment_set_t::iterator seg_iter = getSegIterContaining(pos); + while(seg_iter != mSegments.end()) + { + LLTextSegmentPtr segmentp = *seg_iter; + S32 end = pos + length; + if (segmentp->getStart() < pos) + { + // deleting from middle of segment + if (segmentp->getEnd() > end) + { + segmentp->setEnd(segmentp->getEnd() - length); + } + // truncating segment + else + { + segmentp->setEnd(pos); + } + } + else if (segmentp->getStart() < end) + { + // deleting entire segment + if (segmentp->getEnd() <= end) + { + // remove segment + segmentp->unlinkFromDocument(this); + segment_set_t::iterator seg_to_erase(seg_iter++); + mSegments.erase(seg_to_erase); + continue; + } + // deleting head of segment + else + { + segmentp->setStart(pos); + segmentp->setEnd(segmentp->getEnd() - length); + } + } + else + { + // shifting segments backward to fill deleted portion + segmentp->setStart(segmentp->getStart() - length); + segmentp->setEnd(segmentp->getEnd() - length); + } + ++seg_iter; + } + + getViewModel()->getEditableDisplay().erase(pos, length); + + // recreate default segment in case we erased everything + createDefaultSegment(); + + onValueChange(pos, pos); + needsReflow(pos); + + return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length +} + +S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc) +{ + beforeValueChange(); + + if (pos > (S32)getLength()) + { + return 0; + } + getViewModel()->getEditableDisplay()[pos] = wc; + + onValueChange(pos, pos + 1); + needsReflow(pos); + + return 1; +} + + +void LLTextBase::createDefaultSegment() +{ + // ensures that there is always at least one segment + if (mSegments.empty()) + { + LLStyleConstSP sp(new LLStyle(getStyleParams())); + LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this); + mSegments.insert(default_segment); + default_segment->linkToDocument(this); + } +} + +void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert) +{ + if (segment_to_insert.isNull()) + { + return; + } + + segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart()); + S32 reflow_start_index = 0; + + if (cur_seg_iter == mSegments.end()) + { + mSegments.insert(segment_to_insert); + segment_to_insert->linkToDocument(this); + reflow_start_index = segment_to_insert->getStart(); + } + else + { + LLTextSegmentPtr cur_segmentp = *cur_seg_iter; + reflow_start_index = cur_segmentp->getStart(); + if (cur_segmentp->getStart() < segment_to_insert->getStart()) + { + S32 old_segment_end = cur_segmentp->getEnd(); + // split old at start point for new segment + cur_segmentp->setEnd(segment_to_insert->getStart()); + // advance to next segment + // insert remainder of old segment + LLStyleConstSP sp = cur_segmentp->getStyle(); + LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( sp, segment_to_insert->getStart(), old_segment_end, *this); + mSegments.insert(cur_seg_iter, remainder_segment); + remainder_segment->linkToDocument(this); + // insert new segment before remainder of old segment + mSegments.insert(cur_seg_iter, segment_to_insert); + + segment_to_insert->linkToDocument(this); + // at this point, there will be two overlapping segments owning the text + // associated with the incoming segment + } + else + { + mSegments.insert(cur_seg_iter, segment_to_insert); + segment_to_insert->linkToDocument(this); + } + + // now delete/truncate remaining segments as necessary + // cur_seg_iter points to segment before incoming segment + while(cur_seg_iter != mSegments.end()) + { + cur_segmentp = *cur_seg_iter; + if (cur_segmentp == segment_to_insert) + { + ++cur_seg_iter; + continue; + } + + if (cur_segmentp->getStart() >= segment_to_insert->getStart()) + { + if(cur_segmentp->getEnd() <= segment_to_insert->getEnd()) + { + cur_segmentp->unlinkFromDocument(this); + // grab copy of iterator to erase, and bump it + segment_set_t::iterator seg_to_erase(cur_seg_iter++); + mSegments.erase(seg_to_erase); + continue; + } + else + { + // last overlapping segment, clip to end of incoming segment + // and stop traversal + cur_segmentp->setStart(segment_to_insert->getEnd()); + break; + } + } + ++cur_seg_iter; + } + } + + // layout potentially changed + needsReflow(reflow_start_index); +} + +//virtual +bool LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // handle triple click + if (!mTripleClickTimer.hasExpired()) + { + if (mSkipTripleClick) + { + return true; + } + + S32 real_line = getLineNumFromDocIndex(mCursorPos, false); + S32 line_start = -1; + S32 line_end = -1; + for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end(); + it != end_it; + ++it) + { + if (it->mLineNum < real_line) + { + continue; + } + if (it->mLineNum > real_line) + { + break; + } + if (line_start == -1) + { + line_start = it->mDocIndexStart; + } + line_end = it->mDocIndexEnd; + line_end = llclamp(line_end, 0, getLength()); + } + + if (line_start == -1) + { + return true; + } + + mSelectionEnd = line_start; + mSelectionStart = line_end; + setCursorPos(line_start); + + return true; + } + + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleMouseDown(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleMouseDown(x, y, mask); +} + +//virtual +bool LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (hasMouseCapture() && cur_segment && cur_segment->handleMouseUp(x, y, mask)) + { + // Did we just click on a link? + if (mURLClickSignal + && cur_segment->getStyle() + && cur_segment->getStyle()->isLink()) + { + // *TODO: send URL here? + (*mURLClickSignal)(this, LLSD() ); + } + return true; + } + + return LLUICtrl::handleMouseUp(x, y, mask); +} + +//virtual +bool LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleMiddleMouseDown(x, y, mask); +} + +//virtual +bool LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleMiddleMouseUp(x, y, mask); +} + +//virtual +bool LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleRightMouseDown(x, y, mask); +} + +//virtual +bool LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleRightMouseUp(x, y, mask); +} + +//virtual +bool LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + //Don't start triple click timer if user have clicked on scrollbar + mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); + if (x >= mVisibleTextRect.mLeft && x <= mVisibleTextRect.mRight + && y >= mVisibleTextRect.mBottom && y <= mVisibleTextRect.mTop) + { + mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); + } + + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleDoubleClick(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleDoubleClick(x, y, mask); +} + +//virtual +bool LLTextBase::handleHover(S32 x, S32 y, MASK mask) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleHover(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleHover(x, y, mask); +} + +//virtual +bool LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks)) + { + return true; + } + + return LLUICtrl::handleScrollWheel(x, y, clicks); +} + +//virtual +bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask) +{ + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleToolTip(x, y, mask)) + { + return true; + } + + return LLUICtrl::handleToolTip(x, y, mask); +} + +//virtual +void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent) +{ + if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape) + { + bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; + + LLUICtrl::reshape( width, height, called_from_parent ); + + if (mScroller && scrolled_to_bottom && mTrackEnd) + { + // keep bottom of text buffer visible + // do this here as well as in reflow to handle case + // where shrinking from top, which causes buffer to temporarily + // not be scrolled to the bottom, since the scroll index + // specified the _top_ of the visible document region + mScroller->goToBottom(); + } + + // do this first after reshape, because other things depend on + // up-to-date mVisibleTextRect + updateRects(); + + needsReflow(); + } +} + +//virtual +void LLTextBase::draw() +{ + // reflow if needed, on demand + reflow(); + + // then update scroll position, as cursor may have moved + if (!mReadOnly) + { + updateScrollFromCursor(); + } + + LLRect text_rect; + if (mScroller) + { + mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &text_rect, this); + } + else + { + LLRect visible_lines_rect; + std::pair line_range = getVisibleLines(mClipPartial); + for (S32 i = line_range.first; i < line_range.second; i++) + { + if (visible_lines_rect.isEmpty()) + { + visible_lines_rect = mLineInfoList[i].mRect; + } + else + { + visible_lines_rect.unionWith(mLineInfoList[i].mRect); + } + } + text_rect = visible_lines_rect; + text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); + } + + if (mBGVisible) + { + F32 alpha = getCurrentTransparency(); + // clip background rect against extents, if we support scrolling + LLRect bg_rect = mVisibleTextRect; + if (mScroller) + { + bg_rect.intersectWith(text_rect); + } + LLColor4 bg_color = mReadOnly + ? mReadOnlyBgColor.get() + : hasFocus() + ? mFocusBgColor.get() + : mWriteableBgColor.get(); + gl_rect_2d(text_rect, bg_color % alpha, true); + } + + // Draw highlighted if needed + if( ll::ui::SearchableControl::getHighlighted() ) + { + LLColor4 bg_color = ll::ui::SearchableControl::getHighlightColor(); + LLRect bg_rect = mVisibleTextRect; + if( mScroller ) + bg_rect.intersectWith( text_rect ); + + gl_rect_2d( text_rect, bg_color, true ); + } + + bool should_clip = mClip || mScroller != NULL; + { LLLocalClipRect clip(text_rect, should_clip); + + // draw document view + if (mScroller) + { + drawChild(mScroller); + } + else + { + drawChild(mDocumentView); + } + + drawSelectionBackground(); + drawText(); + drawCursor(); + } + + mDocumentView->setVisibleDirect(false); + LLUICtrl::draw(); + mDocumentView->setVisibleDirect(true); +} + + +//virtual +void LLTextBase::setColor( const LLColor4& c ) +{ + mFgColor = c; + mStyleDirty = true; +} + +//virtual +void LLTextBase::setReadOnlyColor(const LLColor4 &c) +{ + mReadOnlyFgColor = c; + mStyleDirty = true; +} + +//virtual +void LLTextBase::onVisibilityChange( bool new_visibility ) +{ + LLContextMenu* menu = static_cast(mPopupMenuHandle.get()); + if(!new_visibility && menu) + { + menu->hide(); + } + LLUICtrl::onVisibilityChange(new_visibility); +} + +//virtual +void LLTextBase::setValue(const LLSD& value ) +{ + setText(value.asString()); +} + +//virtual +bool LLTextBase::canDeselect() const +{ + return hasSelection(); +} + + +//virtual +void LLTextBase::deselect() +{ + mSelectionStart = 0; + mSelectionEnd = 0; + mIsSelecting = false; +} + +bool LLTextBase::getSpellCheck() const +{ + return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck); +} + +const std::string& LLTextBase::getSuggestion(U32 index) const +{ + return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null; +} + +U32 LLTextBase::getSuggestionCount() const +{ + return mSuggestionList.size(); +} + +void LLTextBase::replaceWithSuggestion(U32 index) +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) + { + deselect(); + // Insert the suggestion in its place + LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]); + insertStringNoUndo(it->first, utf8str_to_wstring(mSuggestionList[index])); + + // Delete the misspelled word + removeStringNoUndo(it->first + (S32)suggestion.length(), it->second - it->first); + + + setCursorPos(it->first + (S32)suggestion.length()); + onSpellCheckPerformed(); + + break; + } + } + mSpellCheckStart = mSpellCheckEnd = -1; +} + +void LLTextBase::addToDictionary() +{ + if (canAddToDictionary()) + { + LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos)); + } +} + +bool LLTextBase::canAddToDictionary() const +{ + return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); +} + +void LLTextBase::addToIgnore() +{ + if (canAddToIgnore()) + { + LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos)); + } +} + +bool LLTextBase::canAddToIgnore() const +{ + return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); +} + +std::string LLTextBase::getMisspelledWord(U32 pos) const +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= pos) && (it->second >= pos) ) + { + return wstring_to_utf8str(getWText().substr(it->first, it->second - it->first)); + } + } + return LLStringUtil::null; +} + +bool LLTextBase::isMisspelledWord(U32 pos) const +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= pos) && (it->second >= pos) ) + { + return true; + } + } + return false; +} + +void LLTextBase::onSpellCheckSettingsChange() +{ + // Recheck the spelling on every change + mMisspellRanges.clear(); + mSpellCheckStart = mSpellCheckEnd = -1; +} + +void LLTextBase::onFocusReceived() +{ + LLUICtrl::onFocusReceived(); + if (!getLength() && !mLabel.empty()) + { + // delete label which is LLLabelTextSegment + clearSegments(); + } +} + +void LLTextBase::onFocusLost() +{ + LLUICtrl::onFocusLost(); + if (!getLength() && !mLabel.empty()) + { + resetLabel(); + } +} + +// Sets the scrollbar from the cursor position +void LLTextBase::updateScrollFromCursor() +{ + // Update scroll position even in read-only mode (when there's no cursor displayed) + // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736. + + if (!mScrollNeeded || !mScroller) + { + return; + } + mScrollNeeded = false; + + // scroll so that the cursor is at the top of the page + LLRect scroller_doc_window = getVisibleDocumentRect(); + LLRect cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); + mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5)); +} + +S32 LLTextBase::getLeftOffset(S32 width) +{ + switch (mHAlign) + { + case LLFontGL::LEFT: + return mHPad; + case LLFontGL::HCENTER: + return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2); + case LLFontGL::RIGHT: + { + // Font's rendering rounds string size, if value gets rounded + // down last symbol might not have enough space to render, + // compensate by adding an extra pixel as padding + const S32 right_padding = 1; + return llmax(mHPad, mVisibleTextRect.getWidth() - width - right_padding); + } + default: + return mHPad; + } +} + +void LLTextBase::reflow() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + updateSegments(); + + if (mReflowIndex == S32_MAX) + { + return; + } + + bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; + + LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); + bool follow_selection = getLocalRect().overlaps(cursor_rect); // cursor is (potentially) visible + + // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing + cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop; + cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom; + + S32 first_line = getFirstVisibleLine(); + + // if scroll anchor not on first line, update it to first character of first line + if ((first_line < mLineInfoList.size()) + && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart + || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd)) + { + mScrollIndex = mLineInfoList[first_line].mDocIndexStart; + } + LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex); + // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing + first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop; + first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom; + + S32 reflow_count = 0; + while(mReflowIndex < S32_MAX) + { + // we can get into an infinite loop if the document height does not monotonically increase + // with decreasing width (embedded ui elements with alternate layouts). In that case, + // we want to stop reflowing after 2 iterations. We use 2, since we need to handle the case + // of introducing a vertical scrollbar causing a reflow with less width. We should also always + // use an even number of iterations to avoid user visible oscillation of the layout + if(++reflow_count > 2) + { + LL_DEBUGS() << "Breaking out of reflow due to possible infinite loop in " << getName() << LL_ENDL; + break; + } + + S32 start_index = mReflowIndex; + mReflowIndex = S32_MAX; + + // shrink document to minimum size (visible portion of text widget) + // to force inlined widgets with follows set to shrink + if (mWordWrap) + { + mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight()); + } + + S32 cur_top = 0; + + segment_set_t::iterator seg_iter = mSegments.begin(); + S32 seg_offset = 0; + S32 line_start_index = 0; + const F32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin + F32 remaining_pixels = text_available_width; + S32 line_count = 0; + + // find and erase line info structs starting at start_index and going to end of document + if (!mLineInfoList.empty()) + { + // find first element whose end comes after start_index + line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare()); + if (iter != mLineInfoList.end()) + { + line_start_index = iter->mDocIndexStart; + line_count = iter->mLineNum; + cur_top = iter->mRect.mTop; + getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); + mLineInfoList.erase(iter, mLineInfoList.end()); + } + } + + S32 line_height = 0; + S32 seg_line_offset = line_count + 1; + + while(seg_iter != mSegments.end()) + { + LLTextSegmentPtr segment = *seg_iter; + + // track maximum height of any segment on this line + S32 cur_index = segment->getStart() + seg_offset; + + // ask segment how many character fit in remaining space + S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, ll_round(remaining_pixels)) : S32_MAX, + seg_offset, + cur_index - line_start_index, + S32_MAX, + line_count - seg_line_offset); + + F32 segment_width; + S32 segment_height; + bool force_newline = segment->getDimensionsF32(seg_offset, character_count, segment_width, segment_height); + // grow line height as necessary based on reported height of this segment + line_height = llmax(line_height, segment_height); + remaining_pixels -= segment_width; + + seg_offset += character_count; + + S32 last_segment_char_on_line = segment->getStart() + seg_offset; + + // Note: make sure text will fit in width - use ceil, but also make sure + // ceil is used only once per line + S32 text_actual_width = llceil(text_available_width - remaining_pixels); + S32 text_left = getLeftOffset(text_actual_width); + LLRect line_rect(text_left, + cur_top, + text_left + text_actual_width, + cur_top - line_height); + + // if we didn't finish the current segment... + if (last_segment_char_on_line < segment->getEnd()) + { + // add line info and keep going + mLineInfoList.push_back(line_info( + line_start_index, + last_segment_char_on_line, + line_rect, + line_count)); + + line_start_index = segment->getStart() + seg_offset; + cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; + remaining_pixels = text_available_width; + line_height = 0; + } + // ...just consumed last segment.. + else if (++segment_set_t::iterator(seg_iter) == mSegments.end()) + { + mLineInfoList.push_back(line_info( + line_start_index, + last_segment_char_on_line, + line_rect, + line_count)); + cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; + break; + } + // ...or finished a segment and there are segments remaining on this line + else + { + // subtract pixels used and increment segment + if (force_newline) + { + mLineInfoList.push_back(line_info( + line_start_index, + last_segment_char_on_line, + line_rect, + line_count)); + line_start_index = segment->getStart() + seg_offset; + cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; + line_height = 0; + remaining_pixels = text_available_width; + } + ++seg_iter; + seg_offset = 0; + seg_line_offset = force_newline ? line_count + 1 : line_count; + } + if (force_newline) + { + line_count++; + } + } + + // calculate visible region for diplaying text + updateRects(); + + for (segment_set_t::iterator segment_it = mSegments.begin(); + segment_it != mSegments.end(); + ++segment_it) + { + LLTextSegmentPtr segmentp = *segment_it; + segmentp->updateLayout(*this); + + } + } + + // apply scroll constraints after reflowing text + if (!hasMouseCapture() && mScroller) + { + if (scrolled_to_bottom && mTrackEnd) + { + // keep bottom of text buffer visible + endOfDoc(); + } + else if (hasSelection() && follow_selection) + { + // keep cursor in same vertical position on screen when selecting text + LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); + LLRect old_cursor_rect = cursor_rect; + old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop; + old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom; + + mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); + } + else + { + // keep first line of text visible + LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex); + + // pass in desired rect in the coordinate frame of the document viewport + LLRect old_first_char_rect = first_char_rect; + old_first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop; + old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom; + + mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect); + } + } + + // reset desired x cursor position + updateCursorXPos(); +} + +LLRect LLTextBase::getTextBoundingRect() +{ + reflow(); + return mTextBoundingRect; +} + + +void LLTextBase::clearSegments() +{ + mSegments.clear(); + createDefaultSegment(); +} + +S32 LLTextBase::getLineStart( S32 line ) const +{ + S32 num_lines = getLineCount(); + if (num_lines == 0) + { + return 0; + } + + line = llclamp(line, 0, num_lines-1); + return mLineInfoList[line].mDocIndexStart; +} + +S32 LLTextBase::getLineEnd( S32 line ) const +{ + S32 num_lines = getLineCount(); + if (num_lines == 0) + { + return 0; + } + + line = llclamp(line, 0, num_lines-1); + return mLineInfoList[line].mDocIndexEnd; +} + + + +S32 LLTextBase::getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap) const +{ + if (mLineInfoList.empty()) + { + return 0; + } + else + { + line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_index, line_end_compare()); + if (include_wordwrap) + { + return iter - mLineInfoList.begin(); + } + else + { + if (iter == mLineInfoList.end()) + { + return mLineInfoList.back().mLineNum; + } + else + { + return iter->mLineNum; + } + } + } +} + +// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line. +S32 LLTextBase::getLineOffsetFromDocIndex( S32 startpos, bool include_wordwrap) const +{ + if (mLineInfoList.empty()) + { + return startpos; + } + else + { + line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare()); + return startpos - iter->mDocIndexStart; + } +} + +S32 LLTextBase::getFirstVisibleLine() const +{ + LLRect visible_region = getVisibleDocumentRect(); + + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); + + return iter - mLineInfoList.begin(); +} + +std::pair LLTextBase::getVisibleLines(bool require_fully_visible) +{ + LLRect visible_region = getVisibleDocumentRect(); + line_list_t::const_iterator first_iter; + line_list_t::const_iterator last_iter; + + // make sure we have an up-to-date mLineInfoList + reflow(); + + if (require_fully_visible) + { + first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_top()); + last_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom()); + } + else + { + first_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); + last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_top()); + } + return std::pair(first_iter - mLineInfoList.begin(), last_iter - mLineInfoList.begin()); +} + + + +LLTextViewModel* LLTextBase::getViewModel() const +{ + return (LLTextViewModel*)mViewModel.get(); +} + +void LLTextBase::addDocumentChild(LLView* view) +{ + mDocumentView->addChild(view); +} + +void LLTextBase::removeDocumentChild(LLView* view) +{ + mDocumentView->removeChild(view); +} + + +void LLTextBase::updateSegments() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + createDefaultSegment(); +} + +void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const +{ + *seg_iter = getSegIterContaining(startpos); + if (*seg_iter == mSegments.end()) + { + *offsetp = 0; + } + else + { + *offsetp = startpos - (**seg_iter)->getStart(); + } +} + +void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ) +{ + *seg_iter = getSegIterContaining(startpos); + if (*seg_iter == mSegments.end()) + { + *offsetp = 0; + } + else + { + *offsetp = startpos - (**seg_iter)->getStart(); + } +} + +LLTextBase::segment_set_t::iterator LLTextBase::getEditableSegIterContaining(S32 index) +{ + segment_set_t::iterator it = getSegIterContaining(index); + segment_set_t::iterator orig_it = it; + + if (it == mSegments.end()) return it; + + if (!(*it)->canEdit() + && index == (*it)->getStart() + && it != mSegments.begin()) + { + it--; + if ((*it)->canEdit()) + { + return it; + } + } + return orig_it; +} + +LLTextBase::segment_set_t::const_iterator LLTextBase::getEditableSegIterContaining(S32 index) const +{ + segment_set_t::const_iterator it = getSegIterContaining(index); + segment_set_t::const_iterator orig_it = it; + if (it == mSegments.end()) return it; + + if (!(*it)->canEdit() + && index == (*it)->getStart() + && it != mSegments.begin()) + { + it--; + if ((*it)->canEdit()) + { + return it; + } + } + return orig_it; +} + +LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index) +{ + static LLPointer index_segment = new LLIndexSegment(); + + // when there are no segments, we return the end iterator, which must be checked by caller + if (mSegments.size() <= 1) { return mSegments.begin(); } + + index_segment->setStart(index); + index_segment->setEnd(index); + segment_set_t::iterator it = mSegments.upper_bound(index_segment); + return it; +} + +LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const +{ + static LLPointer index_segment = new LLIndexSegment(); + + // when there are no segments, we return the end iterator, which must be checked by caller + if (mSegments.size() <= 1) { return mSegments.begin(); } + + index_segment->setStart(index); + index_segment->setEnd(index); + LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(index_segment); + return it; +} + +// Finds the text segment (if any) at the give local screen position +LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line) +{ + // Find the cursor position at the requested local screen position + S32 offset = getDocIndexFromLocalCoord( x, y, false, hit_past_end_of_line); + segment_set_t::iterator seg_iter = getSegIterContaining(offset); + if (seg_iter != mSegments.end()) + { + return *seg_iter; + } + else + { + return LLTextSegmentPtr(); + } +} + +void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) +{ + // work out the XUI menu file to use for this url + LLUrlMatch match; + std::string url = in_url; + if (! LLUrlRegistry::instance().findUrl(url, match)) + { + return; + } + + std::string xui_file = match.getMenuName(); + if (xui_file.empty()) + { + return; + } + + // set up the callbacks for all of the potential menu items, N.B. we + // don't use const ref strings in callbacks in case url goes out of scope + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url)); + registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url)); + registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url)); + registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url, true)); + registrar.add("Url.Block", boost::bind(&LLUrlAction::blockObject, url)); + registrar.add("Url.Unblock", boost::bind(&LLUrlAction::unblockObject, url)); + registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url)); + registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, url)); + registrar.add("Url.AddFriend", boost::bind(&LLUrlAction::addFriend, url)); + registrar.add("Url.RemoveFriend", boost::bind(&LLUrlAction::removeFriend, url)); + registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url)); + registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url)); + registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url)); + registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); + registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url)); + + // create and return the context menu from the XUI file + + LLContextMenu* menu = static_cast(mPopupMenuHandle.get()); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } + llassert(LLMenuGL::sMenuContainer != NULL); + menu = LLUICtrlFactory::getInstance()->createFromFile(xui_file, LLMenuGL::sMenuContainer, + LLMenuHolderGL::child_registry_t::instance()); + if (menu) + { + mPopupMenuHandle = menu->getHandle(); + + if (mIsFriendSignal) + { + bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url))); + LLView* addFriendButton = menu->getChild("add_friend"); + LLView* removeFriendButton = menu->getChild("remove_friend"); + + if (addFriendButton && removeFriendButton) + { + addFriendButton->setEnabled(!isFriend); + removeFriendButton->setEnabled(isFriend); + } + } + + if (mIsObjectBlockedSignal) + { + bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url)); + LLView* blockButton = menu->getChild("block_object"); + LLView* unblockButton = menu->getChild("unblock_object"); + + if (blockButton && unblockButton) + { + blockButton->setVisible(!is_blocked); + unblockButton->setVisible(is_blocked); + } + } + menu->show(x, y); + LLMenuGL::showPopup(this, menu, x, y); + } +} + +void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params) +{ + // clear out the existing text and segments + getViewModel()->setDisplay(LLWStringUtil::null); + + clearSegments(); +// createDefaultSegment(); + + deselect(); + + // append the new text (supports Url linking) + std::string text(utf8str); + LLStringUtil::removeCRLF(text); + + // appendText modifies mCursorPos... + appendText(text, false, input_params); + // ...so move cursor to top after appending text + if (!mTrackEnd) + { + startOfDoc(); + } + + onValueChange(0, getLength()); +} + +// virtual +const std::string& LLTextBase::getText() const +{ + return getViewModel()->getStringValue(); +} + +// IDEVO - icons can be UI image names or UUID sent from +// server with avatar display name +static LLUIImagePtr image_from_icon_name(const std::string& icon_name) +{ + if (LLUUID::validate(icon_name)) + { + return LLUI::getUIImageByID( LLUUID(icon_name) ); + } + else + { + return LLUI::getUIImage(icon_name); + } +} + + +void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + LLStyle::Params style_params(input_params); + style_params.fillFrom(getStyleParams()); + + S32 part = (S32)LLTextParser::WHOLE; + if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). + { + S32 start=0,end=0; + LLUrlMatch match; + std::string text = new_text; + while (LLUrlRegistry::instance().findUrl(text, match, + boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3), isContentTrusted() || mAlwaysShowIcons)) + { + start = match.getStart(); + end = match.getEnd()+1; + + LLStyle::Params link_params(style_params); + link_params.overwriteFrom(match.getStyle()); + + // output the text before the Url + if (start > 0) + { + if (part == (S32)LLTextParser::WHOLE || + part == (S32)LLTextParser::START) + { + part = (S32)LLTextParser::START; + } + else + { + part = (S32)LLTextParser::MIDDLE; + } + std::string subtext=text.substr(0,start); + appendAndHighlightText(subtext, part, style_params); + } + + // add icon before url if need + LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted() || mAlwaysShowIcons); + if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() ) + { + setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon")); + } + + // output the styled Url + appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly()); + bool tooltip_required = !match.getTooltip().empty(); + + // set the tooltip for the Url label + if (tooltip_required) + { + setLastSegmentToolTip(match.getTooltip()); + } + + // show query part of url with gray color only for LLUrlEntryHTTP url entries + std::string label = match.getQuery(); + if (label.size()) + { + link_params.color = LLColor4::grey; + link_params.readonly_color = LLColor4::grey; + appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly()); + + // set the tooltip for the query part of url + if (tooltip_required) + { + setLastSegmentToolTip(match.getTooltip()); + } + } + + // move on to the rest of the text after the Url + if (end < (S32)text.length()) + { + text = text.substr(end,text.length() - end); + end=0; + part=(S32)LLTextParser::END; + } + else + { + break; + } + } + if (part != (S32)LLTextParser::WHOLE) + part=(S32)LLTextParser::END; + if (end < (S32)text.length()) + appendAndHighlightText(text, part, style_params); + } + else + { + appendAndHighlightText(new_text, part, style_params); + } +} + +void LLTextBase::setLastSegmentToolTip(const std::string &tooltip) +{ + segment_set_t::iterator it = getSegIterContaining(getLength()-1); + if (it != mSegments.end()) + { + LLTextSegmentPtr segment = *it; + segment->setToolTip(tooltip); + } +} + +void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + if (new_text.empty()) + return; + + if(prepend_newline) + appendLineBreakSegment(input_params); + appendTextImpl(new_text,input_params); +} + +void LLTextBase::setLabel(const LLStringExplicit& label) +{ + mLabel = label; + resetLabel(); +} + +bool LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text ) +{ + mLabel.setArg(key, text); + return true; +} + +void LLTextBase::resetLabel() +{ + if (useLabel()) + { + clearSegments(); + + LLStyle* style = new LLStyle(getStyleParams()); + style->setColor(mTentativeFgColor); + LLStyleConstSP sp(style); + + LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, mLabel.getWString().length() + 1, *this); + insertSegment(label); + } +} + +bool LLTextBase::useLabel() const +{ + return !getLength() && !mLabel.empty() && !hasFocus(); +} + +void LLTextBase::setFont(const LLFontGL* font) +{ + mFont = font; + mStyleDirty = true; +} + +void LLTextBase::needsReflow(S32 index) +{ + LL_DEBUGS() << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << LL_ENDL; + mReflowIndex = llmin(mReflowIndex, index); +} + +S32 LLTextBase::removeFirstLine() +{ + if (!mLineInfoList.empty()) + { + S32 length = getLineEnd(0); + deselect(); + removeStringNoUndo(0, length); + return length; + } + return 0; +} + +void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params) +{ + segment_vec_t segments; + LLStyleConstSP sp(new LLStyle(style_params)); + segments.push_back(new LLLineBreakTextSegment(sp, getLength())); + + insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments); +} + +void LLTextBase::appendImageSegment(const LLStyle::Params& style_params) +{ + if(getPlainText()) + { + return; + } + segment_vec_t segments; + LLStyleConstSP sp(new LLStyle(style_params)); + segments.push_back(new LLImageTextSegment(sp, getLength(),*this)); + + insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments); +} + +void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo) +{ + segment_vec_t segments; + LLWString widget_wide_text = utf8str_to_wstring(text); + segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + widget_wide_text.size())); + + insertStringNoUndo(getLength(), widget_wide_text, &segments); +} + +void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only) +{ + // Save old state + S32 selection_start = mSelectionStart; + S32 selection_end = mSelectionEnd; + bool was_selecting = mIsSelecting; + S32 cursor_pos = mCursorPos; + S32 old_length = getLength(); + bool cursor_was_at_end = (mCursorPos == old_length); + + deselect(); + + setCursorPos(old_length); + + if (mParseHighlights) + { + LLStyle::Params highlight_params(style_params); + + LLSD pieces = LLTextParser::instance().parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part); + for (S32 i = 0; i < pieces.size(); i++) + { + LLSD color_llsd = pieces[i]["color"]; + LLColor4 lcolor; + lcolor.setValue(color_llsd); + highlight_params.color = lcolor; + + LLWString wide_text; + wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); + + S32 cur_length = getLength(); + LLStyleConstSP sp(new LLStyle(highlight_params)); + LLTextSegmentPtr segmentp; + if (underline_on_hover_only || mSkipLinkUnderline) + { + highlight_params.font.style("NORMAL"); + LLStyleConstSP normal_sp(new LLStyle(highlight_params)); + segmentp = new LLOnHoverChangeableTextSegment(sp, normal_sp, cur_length, cur_length + wide_text.size(), *this); + } + else + { + segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this); + } + segment_vec_t segments; + segments.push_back(segmentp); + insertStringNoUndo(cur_length, wide_text, &segments); + } + } + else + { + LLWString wide_text; + wide_text = utf8str_to_wstring(new_text); + + segment_vec_t segments; + S32 segment_start = old_length; + S32 segment_end = old_length + wide_text.size(); + LLStyleConstSP sp(new LLStyle(style_params)); + if (underline_on_hover_only || mSkipLinkUnderline) + { + LLStyle::Params normal_style_params(style_params); + normal_style_params.font.style("NORMAL"); + LLStyleConstSP normal_sp(new LLStyle(normal_style_params)); + segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this)); + } + else + { + segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this)); + } + + insertStringNoUndo(getLength(), wide_text, &segments); + } + + // Set the cursor and scroll position + if (selection_start != selection_end) + { + mSelectionStart = selection_start; + mSelectionEnd = selection_end; + + mIsSelecting = was_selecting; + setCursorPos(cursor_pos); + } + else if (cursor_was_at_end) + { + setCursorPos(getLength()); + } + else + { + setCursorPos(cursor_pos); + } +} + +void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only) +{ + if (new_text.empty()) + { + return; + } + + std::string::size_type start = 0; + std::string::size_type pos = new_text.find("\n", start); + + while (pos != std::string::npos) + { + if (pos != start) + { + std::string str = std::string(new_text,start,pos-start); + appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only); + } + appendLineBreakSegment(style_params); + start = pos+1; + pos = new_text.find("\n", start); + } + + std::string str = std::string(new_text, start, new_text.length() - start); + appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only); +} + + +void LLTextBase::replaceUrl(const std::string &url, + const std::string &label, + const std::string &icon) +{ + // get the full (wide) text for the editor so we can change it + LLWString text = getWText(); + LLWString wlabel = utf8str_to_wstring(label); + bool modified = false; + S32 seg_start = 0; + + // iterate through each segment looking for ones styled as links + segment_set_t::iterator it; + for (it = mSegments.begin(); it != mSegments.end(); ++it) + { + LLTextSegment *seg = *it; + LLStyleConstSP style = seg->getStyle(); + + // update segment start/end length in case we replaced text earlier + S32 seg_length = seg->getEnd() - seg->getStart(); + seg->setStart(seg_start); + seg->setEnd(seg_start + seg_length); + + // if we find a link with our Url, then replace the label + if (style->getLinkHREF() == url) + { + S32 start = seg->getStart(); + S32 end = seg->getEnd(); + text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1); + seg->setEnd(start + wlabel.size()); + modified = true; + } + + // Icon might be updated when more avatar or group info + // becomes available + if (style->isImage() && style->getLinkHREF() == url) + { + LLUIImagePtr image = image_from_icon_name( icon ); + if (image) + { + LLStyle::Params icon_params; + icon_params.image = image; + LLStyleConstSP new_style(new LLStyle(icon_params)); + seg->setStyle(new_style); + modified = true; + } + } + + // work out the character offset for the next segment + seg_start = seg->getEnd(); + } + + // update the editor with the new (wide) text string + if (modified) + { + getViewModel()->setDisplay(text); + deselect(); + setCursorPos(mCursorPos); + needsReflow(); + } +} + + +void LLTextBase::setWText(const LLWString& text) +{ + setText(wstring_to_utf8str(text)); +} + +const LLWString& LLTextBase::getWText() const +{ + return getViewModel()->getDisplay(); +} + +// If round is true, if the position is on the right half of a character, the cursor +// will be put to its right. If round is false, the cursor will always be put to the +// character's left. + +S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line) const +{ + // Figure out which line we're nearest to. + LLRect doc_rect = mDocumentView->getRect(); + S32 doc_y = local_y - doc_rect.mBottom; + + // binary search for line that starts before local_y + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_y, compare_bottom()); + + if (!mLineInfoList.size() || line_iter == mLineInfoList.end()) + { + return getLength(); // past the end + } + + S32 pos = getLength(); + F32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft; + + segment_set_t::iterator line_seg_iter; + S32 line_seg_offset; + for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); + line_seg_iter != mSegments.end(); + ++line_seg_iter, line_seg_offset = 0) + { + const LLTextSegmentPtr segmentp = *line_seg_iter; + + S32 segment_line_start = segmentp->getStart() + line_seg_offset; + S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start; + F32 text_width; + S32 text_height; + bool newline = segmentp->getDimensionsF32(line_seg_offset, segment_line_length, text_width, text_height); + + if(newline) + { + pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); + break; + } + + // if we've reached a line of text *below* the mouse cursor, doc index is first character on that line + if (hit_past_end_of_line && doc_y > line_iter->mRect.mTop) + { + pos = segment_line_start; + break; + } + if (local_x < start_x + text_width) // cursor to left of right edge of text + { + // Figure out which character we're nearest to. + S32 offset; + if (!segmentp->canEdit()) + { + F32 segment_width; + S32 segment_height; + segmentp->getDimensionsF32(0, segmentp->getEnd() - segmentp->getStart(), segment_width, segment_height); + if (round && local_x - start_x > segment_width / 2) + { + offset = segment_line_length; + } + else + { + offset = 0; + } + } + else + { + offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); + } + pos = segment_line_start + offset; + break; + } + else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd) + { + if (getLineNumFromDocIndex(line_iter->mDocIndexEnd - 1) == line_iter->mLineNum) + { + // if segment wraps to the next line we should step one char back + // to compensate for the space char between words + // which is removed due to wrapping + pos = llclamp(line_iter->mDocIndexEnd - 1, 0, getLength()); + } + else + { + pos = llclamp(line_iter->mDocIndexEnd, 0, getLength()); + } + break; + } + start_x += text_width; + } + + return pos; +} + +// returns rectangle of insertion caret +// in document coordinate frame from given index into text +LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const +{ + if (mLineInfoList.empty()) + { + return LLRect(); + } + + // clamp pos to valid values + pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); + + line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare()); + + segment_set_t::iterator line_seg_iter; + S32 line_seg_offset; + segment_set_t::iterator cursor_seg_iter; + S32 cursor_seg_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); + getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset); + + F32 doc_left_precise = line_iter->mRect.mLeft; + + while(line_seg_iter != mSegments.end()) + { + const LLTextSegmentPtr segmentp = *line_seg_iter; + + if (line_seg_iter == cursor_seg_iter) + { + // cursor advanced to right based on difference in offset of cursor to start of line + F32 segment_width; + S32 segment_height; + segmentp->getDimensionsF32(line_seg_offset, cursor_seg_offset - line_seg_offset, segment_width, segment_height); + doc_left_precise += segment_width; + + break; + } + else + { + // add remainder of current text segment to cursor position + F32 segment_width; + S32 segment_height; + segmentp->getDimensionsF32(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset, segment_width, segment_height); + doc_left_precise += segment_width; + // offset will be 0 for all segments after the first + line_seg_offset = 0; + // go to next text segment on this line + ++line_seg_iter; + } + } + + LLRect doc_rect; + doc_rect.mLeft = doc_left_precise; + doc_rect.mBottom = line_iter->mRect.mBottom; + doc_rect.mTop = line_iter->mRect.mTop; + + // set rect to 0 width + doc_rect.mRight = doc_rect.mLeft; + + return doc_rect; +} + +LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const +{ + LLRect content_window_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); + if (mBorderVisible) + { + content_window_rect.stretch(-1); + } + + LLRect local_rect; + + if (mLineInfoList.empty()) + { + // return default height rect in upper left + local_rect = content_window_rect; + local_rect.mBottom = local_rect.mTop - mFont->getLineHeight(); + return local_rect; + } + + // get the rect in document coordinates + LLRect doc_rect = getDocRectFromDocIndex(pos); + + // compensate for scrolled, inset view of doc + LLRect scrolled_view_rect = getVisibleDocumentRect(); + local_rect = doc_rect; + local_rect.translate(content_window_rect.mLeft - scrolled_view_rect.mLeft, + content_window_rect.mBottom - scrolled_view_rect.mBottom); + + return local_rect; +} + +void LLTextBase::updateCursorXPos() +{ + // reset desired x cursor position + mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft; +} + + +void LLTextBase::startOfLine() +{ + S32 offset = getLineOffsetFromDocIndex(mCursorPos); + setCursorPos(mCursorPos - offset); +} + +void LLTextBase::endOfLine() +{ + S32 line = getLineNumFromDocIndex(mCursorPos); + S32 num_lines = getLineCount(); + if (line + 1 >= num_lines) + { + setCursorPos(getLength()); + } + else + { + setCursorPos( getLineStart(line + 1) - 1 ); + } +} + +void LLTextBase::startOfDoc() +{ + setCursorPos(0); + if (mScroller) + { + mScroller->goToTop(); + } +} + +void LLTextBase::endOfDoc() +{ + setCursorPos(getLength()); + if (mScroller) + { + mScroller->goToBottom(); + } +} + +void LLTextBase::changePage( S32 delta ) +{ + const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10; + if (delta == 0 || !mScroller) return; + + LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); + + if( delta == -1 ) + { + mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE); + } + else + if( delta == 1 ) + { + mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE); + } + + if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect) + { + // cursor didn't change apparent position, so move to top or bottom of document, respectively + if (delta < 0) + { + startOfDoc(); + } + else + { + endOfDoc(); + } + } + else + { + setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false); + } +} + +// Picks a new cursor position based on the screen size of text being drawn. +void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset ) +{ + setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset); +} + + +void LLTextBase::changeLine( S32 delta ) +{ + S32 line = getLineNumFromDocIndex(mCursorPos); + S32 max_line_nb = getLineCount() - 1; + max_line_nb = (max_line_nb < 0 ? 0 : max_line_nb); + + S32 new_line = llclamp(line + delta, 0, max_line_nb); + + if (new_line != line) + { + LLRect visible_region = getVisibleDocumentRect(); + S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, + mLineInfoList[new_line].mRect.mBottom + mVisibleTextRect.mBottom - visible_region.mBottom, true); + S32 actual_line = getLineNumFromDocIndex(new_cursor_pos); + if (actual_line != new_line) + { + // line edge, correcting position by 1 to move onto proper line + new_cursor_pos += new_line - actual_line; + } + setCursorPos(new_cursor_pos, true); + } +} + +bool LLTextBase::scrolledToStart() +{ + return mScroller->isAtTop(); +} + +bool LLTextBase::scrolledToEnd() +{ + return mScroller->isAtBottom(); +} + +bool LLTextBase::setCursor(S32 row, S32 column) +{ + if (row < 0 || column < 0) return false; + + S32 n_lines = mLineInfoList.size(); + for (S32 line = row; line < n_lines; ++line) + { + const line_info& li = mLineInfoList[line]; + + if (li.mLineNum < row) + { + continue; + } + else if (li.mLineNum > row) + { + break; // invalid column specified + } + + // Found the given row. + S32 line_length = li.mDocIndexEnd - li.mDocIndexStart;; + if (column >= line_length) + { + column -= line_length; + continue; + } + + // Found the given column. + updateCursorXPos(); + S32 doc_pos = li.mDocIndexStart + column; + return setCursorPos(doc_pos); + } + + return false; // invalid row or column specified +} + + +bool LLTextBase::setCursorPos(S32 cursor_pos, bool keep_cursor_offset) +{ + S32 new_cursor_pos = cursor_pos; + if (new_cursor_pos != mCursorPos) + { + new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos); + } + + mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength()); + needsScroll(); + if (!keep_cursor_offset) + updateCursorXPos(); + // did we get requested position? + return new_cursor_pos == cursor_pos; +} + +// constraint cursor to editable segments of document +S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction) +{ + segment_set_t::iterator segment_iter; + S32 offset; + getSegmentAndOffset(index, &segment_iter, &offset); + if (segment_iter == mSegments.end()) + { + return 0; + } + + LLTextSegmentPtr segmentp = *segment_iter; + + if (segmentp->canEdit()) + { + return segmentp->getStart() + offset; + } + else if (segmentp->getStart() < index && index < segmentp->getEnd()) + { + // bias towards document end + if (increasing_direction) + { + return segmentp->getEnd(); + } + // bias towards document start + else + { + return segmentp->getStart(); + } + } + else + { + return index; + } +} + +void LLTextBase::updateRects() +{ + LLRect old_text_rect = mVisibleTextRect; + mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); + + if (mLineInfoList.empty()) + { + mTextBoundingRect = LLRect(0, mVPad, mHPad, 0); + } + else + { + mTextBoundingRect = mLineInfoList.begin()->mRect; + for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin(); + line_iter != mLineInfoList.end(); + ++line_iter) + { + mTextBoundingRect.unionWith(line_iter->mRect); + } + + mTextBoundingRect.mTop += mVPad; + + S32 delta_pos = 0; + + switch(mVAlign) + { + case LLFontGL::TOP: + delta_pos = llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom); + break; + case LLFontGL::VCENTER: + delta_pos = (llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom) + (mVisibleTextRect.mBottom - mTextBoundingRect.mBottom)) / 2; + break; + case LLFontGL::BOTTOM: + delta_pos = mVisibleTextRect.mBottom - mTextBoundingRect.mBottom; + break; + case LLFontGL::BASELINE: + // do nothing + break; + } + // move line segments to fit new document rect + for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) + { + it->mRect.translate(0, delta_pos); + } + mTextBoundingRect.translate(0, delta_pos); + } + + // update document container dimensions according to text contents + LLRect doc_rect; + // use old mVisibleTextRect constraint document to width of viewable region + doc_rect.mBottom = llmin(mVisibleTextRect.mBottom, mTextBoundingRect.mBottom); + doc_rect.mLeft = 0; + + // allow horizontal scrolling? + // if so, use entire width of text contents + // otherwise, stop at width of mVisibleTextRect + //FIXME: consider use of getWordWrap() instead + doc_rect.mRight = mScroller + ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight) + : mVisibleTextRect.getWidth(); + doc_rect.mTop = llmax(mVisibleTextRect.mTop, mTextBoundingRect.mTop); + + if (!mScroller) + { + // push doc rect to top of text widget + switch(mVAlign) + { + case LLFontGL::TOP: + doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop); + break; + case LLFontGL::VCENTER: + doc_rect.translate(0, (mVisibleTextRect.getHeight() - doc_rect.mTop) / 2); + case LLFontGL::BOTTOM: + default: + break; + } + } + + mDocumentView->setShape(doc_rect); + + //update mVisibleTextRect *after* mDocumentView has been resized + // so that scrollbars are added if document needs to scroll + // since mVisibleTextRect does not include scrollbars + mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); + //FIXME: replace border with image? + if (mBorderVisible) + { + mVisibleTextRect.stretch(-1); + } + if (mVisibleTextRect != old_text_rect) + { + needsReflow(); + } + + // update mTextBoundingRect after mVisibleTextRect took scrolls into account + if (!mLineInfoList.empty() && mScroller) + { + S32 delta_pos = 0; + + switch(mVAlign) + { + case LLFontGL::TOP: + delta_pos = llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom); + break; + case LLFontGL::VCENTER: + delta_pos = (llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom) + (mVisibleTextRect.mBottom - mTextBoundingRect.mBottom)) / 2; + break; + case LLFontGL::BOTTOM: + delta_pos = mVisibleTextRect.mBottom - mTextBoundingRect.mBottom; + break; + case LLFontGL::BASELINE: + // do nothing + break; + } + // move line segments to fit new visible rect + if (delta_pos != 0) + { + for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) + { + it->mRect.translate(0, delta_pos); + } + mTextBoundingRect.translate(0, delta_pos); + } + } + + // update document container again, using new mVisibleTextRect (that has scrollbars enabled as needed) + doc_rect.mBottom = llmin(mVisibleTextRect.mBottom, mTextBoundingRect.mBottom); + doc_rect.mLeft = 0; + doc_rect.mRight = mScroller + ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight) + : mVisibleTextRect.getWidth(); + doc_rect.mTop = llmax(mVisibleTextRect.getHeight(), mTextBoundingRect.getHeight()) + doc_rect.mBottom; + if (!mScroller) + { + // push doc rect to top of text widget + switch(mVAlign) + { + case LLFontGL::TOP: + doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop); + break; + case LLFontGL::VCENTER: + doc_rect.translate(0, (mVisibleTextRect.getHeight() - doc_rect.mTop) / 2); + case LLFontGL::BOTTOM: + default: + break; + } + } + mDocumentView->setShape(doc_rect); +} + + +void LLTextBase::startSelection() +{ + if( !mIsSelecting ) + { + mIsSelecting = true; + mSelectionStart = mCursorPos; + mSelectionEnd = mCursorPos; + } +} + +void LLTextBase::endSelection() +{ + if( mIsSelecting ) + { + mIsSelecting = false; + mSelectionEnd = mCursorPos; + } +} + +// get portion of document that is visible in text editor +LLRect LLTextBase::getVisibleDocumentRect() const +{ + if (mScroller) + { + return mScroller->getVisibleContentRect(); + } + else if (mClip) + { + LLRect visible_text_rect = getVisibleTextRect(); + LLRect doc_rect = mDocumentView->getRect(); + visible_text_rect.translate(-doc_rect.mLeft, -doc_rect.mBottom); + + // reject partially visible lines + LLRect visible_lines_rect; + for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end(); + it != end_it; + ++it) + { + bool line_visible = mClipPartial ? visible_text_rect.contains(it->mRect) : visible_text_rect.overlaps(it->mRect); + if (line_visible) + { + if (visible_lines_rect.isEmpty()) + { + visible_lines_rect = it->mRect; + } + else + { + visible_lines_rect.unionWith(it->mRect); + } + } + } + return visible_lines_rect; + } + else + { // entire document rect is visible + // but offset according to height of widget + + LLRect doc_rect = mDocumentView->getLocalRect(); + doc_rect.mLeft -= mDocumentView->getRect().mLeft; + // adjust for height of text above widget baseline + doc_rect.mBottom = doc_rect.getHeight() - mVisibleTextRect.getHeight(); + return doc_rect; + } +} + +boost::signals2::connection LLTextBase::setURLClickedCallback(const commit_signal_t::slot_type& cb) +{ + if (!mURLClickSignal) + { + mURLClickSignal = new commit_signal_t(); + } + return mURLClickSignal->connect(cb); +} + +boost::signals2::connection LLTextBase::setIsFriendCallback(const is_friend_signal_t::slot_type& cb) +{ + if (!mIsFriendSignal) + { + mIsFriendSignal = new is_friend_signal_t(); + } + return mIsFriendSignal->connect(cb); +} + +boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb) +{ + if (!mIsObjectBlockedSignal) + { + mIsObjectBlockedSignal = new is_blocked_signal_t(); + } + return mIsObjectBlockedSignal->connect(cb); +} + +// +// LLTextSegment +// + +LLTextSegment::~LLTextSegment() +{} + +bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = 0; return false; } +bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +{ + F32 fwidth = 0; + bool result = getDimensionsF32(first_char, num_chars, fwidth, height); + width = ll_round(fwidth); + return result; +} + +S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; } +S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const { return 0; } +void LLTextSegment::updateLayout(const LLTextBase& editor) {} +F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; } +bool LLTextSegment::canEdit() const { return false; } +void LLTextSegment::unlinkFromDocument(LLTextBase*) {} +void LLTextSegment::linkToDocument(LLTextBase*) {} +const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; } +//void LLTextSegment::setColor(const LLColor4 &color) {} +LLStyleConstSP LLTextSegment::getStyle() const {static LLStyleConstSP sp(new LLStyle()); return sp; } +void LLTextSegment::setStyle(LLStyleConstSP style) {} +void LLTextSegment::setToken( LLKeywordToken* token ) {} +LLKeywordToken* LLTextSegment::getToken() const { return NULL; } +void LLTextSegment::setToolTip( const std::string &msg ) {} +void LLTextSegment::dump() const {} +bool LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return false; } +bool LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return false; } +bool LLTextSegment::handleScrollHWheel(S32 x, S32 y, S32 clicks) { return false; } +bool LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return false; } +const std::string& LLTextSegment::getName() const +{ + return LLStringUtil::null; +} +void LLTextSegment::onMouseCaptureLost() {} +void LLTextSegment::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {} +void LLTextSegment::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {} +bool LLTextSegment::hasMouseCapture() { return false; } + +// +// LLNormalTextSegment +// + +LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ) +: LLTextSegment(start, end), + mStyle( style ), + mToken(NULL), + mEditor(editor) +{ + mFontHeight = mStyle->getFont()->getLineHeight(); + + LLUIImagePtr image = mStyle->getImage(); + if (image.notNull()) + { + mImageLoadedConnection = image->addLoadedCallback(boost::bind(&LLTextBase::needsReflow, &mEditor, start)); + } +} + +LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) +: LLTextSegment(start, end), + mToken(NULL), + mEditor(editor) +{ + mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color)); + + mFontHeight = mStyle->getFont()->getLineHeight(); +} + +LLNormalTextSegment::~LLNormalTextSegment() +{ + mImageLoadedConnection.disconnect(); +} + + +F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) +{ + if( end - start > 0 ) + { + return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect); + } + return draw_rect.mLeft; +} + +// Draws a single text segment, reversing the color for selection if needed. +F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect) +{ + F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; + + const LLWString &text = getWText(); + + F32 right_x = rect.mLeft; + if (!mStyle->isVisible()) + { + return right_x; + } + + const LLFontGL* font = mStyle->getFont(); + + LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha; + + if( selection_start > seg_start ) + { + // Draw normally + S32 start = seg_start; + S32 end = llmin( selection_start, seg_end ); + S32 length = end - start; + font->render(text, start, + rect, + color, + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } + rect.mLeft = right_x; + + if( (selection_start < seg_end) && (selection_end > seg_start) ) + { + // Draw reversed + S32 start = llmax( selection_start, seg_start ); + S32 end = llmin( selection_end, seg_end ); + S32 length = end - start; + + font->render(text, start, + rect, + mStyle->getSelectedColor().get(), + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + LLFontGL::NO_SHADOW, + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } + rect.mLeft = right_x; + if( selection_end < seg_end ) + { + // Draw normally + S32 start = llmax( selection_end, seg_start ); + S32 end = seg_end; + S32 length = end - start; + font->render(text, start, + rect, + color, + LLFontGL::LEFT, mEditor.mTextVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses(), + mEditor.getUseColor()); + } + return right_x; +} + +bool LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask) +{ + if (getStyle() && getStyle()->isLink()) + { + // Only process the click if it's actually in this segment, not to the right of the end-of-line. + if(mEditor.getSegmentAtLocalPos(x, y, false) == this) + { + LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND); + return true; + } + } + return false; +} + +bool LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (getStyle() && getStyle()->isLink()) + { + // Only process the click if it's actually in this segment, not to the right of the end-of-line. + if(mEditor.getSegmentAtLocalPos(x, y, false) == this) + { + mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF()); + return true; + } + } + return false; +} + +bool LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (getStyle() && getStyle()->isLink()) + { + // Only process the click if it's actually in this segment, not to the right of the end-of-line. + if(mEditor.getSegmentAtLocalPos(x, y, false) == this) + { + // eat mouse down event on hyperlinks, so we get the mouse up + return true; + } + } + + return false; +} + +bool LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (getStyle() && getStyle()->isLink()) + { + // Only process the click if it's actually in this segment, not to the right of the end-of-line. + if(mEditor.getSegmentAtLocalPos(x, y, false) == this) + { + std::string url = getStyle()->getLinkHREF(); + if (!mEditor.mForceUrlsExternal) + { + LLUrlAction::clickAction(url, mEditor.isContentTrusted()); + } + else if (!LLUrlAction::executeSLURL(url, mEditor.isContentTrusted())) + { + LLUrlAction::openURLExternal(url); + } + return true; + } + } + + return false; +} + +bool LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ + std::string msg; + // do we have a tooltip for a loaded keyword (for script editor)? + if (mToken && !mToken->getToolTip().empty()) + { + const LLWString& wmsg = mToken->getToolTip(); + LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg), (mToken->getType() == LLKeywordToken::TT_FUNCTION)); + return true; + } + // or do we have an explicitly set tooltip (e.g., for Urls) + if (!mTooltip.empty()) + { + LLToolTipMgr::instance().show(mTooltip); + return true; + } + + return false; +} + +void LLNormalTextSegment::setToolTip(const std::string& tooltip) +{ + // we cannot replace a keyword tooltip that's loaded from a file + if (mToken) + { + LL_WARNS() << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << LL_ENDL; + return; + } + mTooltip = tooltip; +} + +bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +{ + height = 0; + width = 0; + if (num_chars > 0) + { + height = mFontHeight; + const LLWString &text = getWText(); + // if last character is a newline, then return true, forcing line break + width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars, true); + } + return false; +} + +S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const +{ + const LLWString &text = getWText(); + return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset, + (F32)segment_local_x_coord, + F32_MAX, + num_chars, + round); +} + +S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const +{ + const LLWString &text = getWText(); + + LLUIImagePtr image = mStyle->getImage(); + if( image.notNull()) + { + num_pixels = llmax(0, num_pixels - image->getWidth()); + } + + S32 last_char = mEnd; + + // set max characters to length of segment, or to first newline + max_chars = llmin(max_chars, last_char - (mStart + segment_offset)); + + // if no character yet displayed on this line, don't require word wrapping since + // we can just move to the next line, otherwise insist on it so we make forward progress + LLFontGL::EWordWrapStyle word_wrap_style = (line_offset == 0) + ? LLFontGL::WORD_BOUNDARY_IF_POSSIBLE + : LLFontGL::ONLY_WORD_BOUNDARIES; + + + S32 offsetLength = text.length() - (segment_offset + mStart); + + if(getLength() < segment_offset + mStart) + { + LL_INFOS() << "getLength() < segment_offset + mStart\t getLength()\t" << getLength() << "\tsegment_offset:\t" + << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << "\tmax_chars\t" << max_chars << LL_ENDL; + } + + if( (offsetLength + 1) < max_chars) + { + LL_INFOS() << "offsetString.length() + 1 < max_chars\t max_chars:\t" << max_chars << "\toffsetString.length():\t" << offsetLength << " getLength() : " + << getLength() << "\tsegment_offset:\t" << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << LL_ENDL; + } + + S32 num_chars = mStyle->getFont()->maxDrawableChars( text.c_str() + (segment_offset + mStart), + (F32)num_pixels, + max_chars, + word_wrap_style); + + if (num_chars == 0 + && line_offset == 0 + && max_chars > 0) + { + // If at the beginning of a line, and a single character won't fit, draw it anyway + num_chars = 1; + } + + // include *either* the EOF or newline character in this run of text + // but not both + S32 last_char_in_run = mStart + segment_offset + num_chars; + // check length first to avoid indexing off end of string + if (last_char_in_run < mEnd + && (last_char_in_run >= getLength())) + { + num_chars++; + } + return num_chars; +} + +void LLNormalTextSegment::dump() const +{ + LL_INFOS() << "Segment [" << +// mColor.mV[VX] << ", " << +// mColor.mV[VY] << ", " << +// mColor.mV[VZ] << "]\t[" << + mStart << ", " << + getEnd() << "]" << + LL_ENDL; +} + +/*virtual*/ +const LLWString& LLNormalTextSegment::getWText() const +{ + return mEditor.getWText(); +} + +/*virtual*/ +const S32 LLNormalTextSegment::getLength() const +{ + return mEditor.getLength(); +} + +LLLabelTextSegment::LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ) +: LLNormalTextSegment(style, start, end, editor) +{ +} + +LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) +: LLNormalTextSegment(color, start, end, editor, is_visible) +{ +} + +/*virtual*/ +const LLWString& LLLabelTextSegment::getWText() const +{ + return mEditor.getWlabel(); +} +/*virtual*/ +const S32 LLLabelTextSegment::getLength() const +{ + return mEditor.getWlabel().length(); +} + +// +// LLEmojiTextSegment +// +LLEmojiTextSegment::LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor) + : LLNormalTextSegment(style, start, end, editor) +{ +} + +LLEmojiTextSegment::LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) + : LLNormalTextSegment(color, start, end, editor, is_visible) +{ +} + +bool LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ + if (mTooltip.empty()) + { + LLWString emoji = getWText().substr(getStart(), getEnd() - getStart()); + if (!emoji.empty()) + { + mTooltip = LLEmojiHelper::instance().getToolTip(emoji[0]); + } + } + + return LLNormalTextSegment::handleToolTip(x, y, mask); +} + +// +// LLOnHoverChangeableTextSegment +// + +LLOnHoverChangeableTextSegment::LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ): + LLNormalTextSegment(normal_style, start, end, editor), + mHoveredStyle(style), + mNormalStyle(normal_style){} + +/*virtual*/ +F32 LLOnHoverChangeableTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) +{ + F32 result = LLNormalTextSegment::draw(start, end, selection_start, selection_end, draw_rect); + if (end == mEnd - mStart) + { + mStyle = mNormalStyle; + } + return result; +} + +/*virtual*/ +bool LLOnHoverChangeableTextSegment::handleHover(S32 x, S32 y, MASK mask) +{ + mStyle = mEditor.getSkipLinkUnderline() ? mNormalStyle : mHoveredStyle; + return LLNormalTextSegment::handleHover(x, y, mask); +} + + +// +// LLInlineViewSegment +// + +LLInlineViewSegment::LLInlineViewSegment(const Params& p, S32 start, S32 end) +: LLTextSegment(start, end), + mView(p.view), + mForceNewLine(p.force_newline), + mLeftPad(p.left_pad), + mRightPad(p.right_pad), + mTopPad(p.top_pad), + mBottomPad(p.bottom_pad) +{ +} + +LLInlineViewSegment::~LLInlineViewSegment() +{ + mView->die(); +} + +bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +{ + if (first_char == 0 && num_chars == 0) + { + // We didn't fit on a line or were forced to new string + // the widget will fall on the next line, so width here is 0 + width = 0; + + if (mForceNewLine) + { + // Chat, string can't be smaller then font height even if it is empty + LLStyleSP s(new LLStyle(LLStyle::Params().visible(true))); + height = s->getFont()->getLineHeight(); + + return true; // new line + } + else + { + // height from previous segment in same string will be used, word-wrap + height = 0; + } + + } + else + { + width = mLeftPad + mRightPad + mView->getRect().getWidth(); + height = mBottomPad + mTopPad + mView->getRect().getHeight(); + } + + return false; +} + +S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const +{ + // if putting a widget anywhere but at the beginning of a line + // and the widget doesn't fit or mForceNewLine is true + // then return 0 chars for that line, and all characters for the next + if (mForceNewLine && line_ind == 0) + { + return 0; + } + else if (line_offset != 0 && num_pixels < mView->getRect().getWidth()) + { + return 0; + } + else + { + return mEnd - mStart; + } +} + +void LLInlineViewSegment::updateLayout(const LLTextBase& editor) +{ + LLRect start_rect = editor.getDocRectFromDocIndex(mStart); + mView->setOrigin(start_rect.mLeft + mLeftPad, start_rect.mBottom + mBottomPad); +} + +F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) +{ + // return padded width of widget + // widget is actually drawn during mDocumentView's draw() + return (F32)(draw_rect.mLeft + mView->getRect().getWidth() + mLeftPad + mRightPad); +} + +void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor) +{ + editor->removeDocumentChild(mView); +} + +void LLInlineViewSegment::linkToDocument(LLTextBase* editor) +{ + editor->addDocumentChild(mView); +} + +LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1) +{ + LLStyleSP s( new LLStyle(LLStyle::Params().visible(true))); + + mFontHeight = s->getFont()->getLineHeight(); +} +LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1) +{ + mFontHeight = style->getFont()->getLineHeight(); +} +LLLineBreakTextSegment::~LLLineBreakTextSegment() +{ +} +bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +{ + width = 0; + height = mFontHeight; + + return true; +} +S32 LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const +{ + return 1; +} +F32 LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) +{ + return draw_rect.mLeft; +} + +LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor) +: LLTextSegment(pos,pos+1), + mStyle( style ), + mEditor(editor) +{ +} + +LLImageTextSegment::~LLImageTextSegment() +{ +} + +static const S32 IMAGE_HPAD = 3; + +bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +{ + width = 0; + height = mStyle->getFont()->getLineHeight(); + + LLUIImagePtr image = mStyle->getImage(); + if( num_chars>0 && image.notNull()) + { + width += image->getWidth() + IMAGE_HPAD; + height = llmax(height, image->getHeight() + IMAGE_HPAD ); + } + return false; +} + +S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const +{ + LLUIImagePtr image = mStyle->getImage(); + + if (image.isNull()) + { + return 1; + } + + S32 image_width = image->getWidth(); + if(line_offset == 0 || num_pixels>image_width + IMAGE_HPAD) + { + return 1; + } + + return 0; +} + +bool LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ + if (!mTooltip.empty()) + { + LLToolTipMgr::instance().show(mTooltip); + return true; + } + + return false; +} + +void LLImageTextSegment::setToolTip(const std::string& tooltip) +{ + mTooltip = tooltip; +} + +F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) +{ + if ( (start >= 0) && (end <= mEnd - mStart)) + { + LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha; + LLUIImagePtr image = mStyle->getImage(); + if (image.notNull()) + { + S32 style_image_height = image->getHeight(); + S32 style_image_width = image->getWidth(); + // Text is drawn from the top of the draw_rect downward + + S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2); + // Align image to center of draw rect + S32 image_bottom = text_center - (style_image_height / 2); + image->draw(draw_rect.mLeft, image_bottom, + style_image_width, style_image_height, color); + + const S32 IMAGE_HPAD = 3; + return draw_rect.mLeft + style_image_width + IMAGE_HPAD; + } + } + return 0.0; +} + +void LLTextBase::setWordWrap(bool wrap) +{ + mWordWrap = wrap; +} diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index d8e9027bae..f6c7ce6e81 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -1,758 +1,758 @@ -/** - * @file lltextbase.h - * @author Martin Reddy - * @brief The base class of text box/editor, providing Url handling support - * - * $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_LLTEXTBASE_H -#define LL_LLTEXTBASE_H - -#include "v4color.h" -#include "lleditmenuhandler.h" -#include "llspellcheckmenuhandler.h" -#include "llstyle.h" -#include "llkeywords.h" -#include "llpanel.h" - -#include -#include -#include - -#include - -class LLScrollContainer; -class LLContextMenu; -class LLUrlMatch; - -/// -/// A text segment is used to specify a subsection of a text string -/// that should be formatted differently, such as a hyperlink. It -/// includes a start/end offset from the start of the string, a -/// style to render with, an optional tooltip, etc. -/// -class LLTextSegment -: public LLRefCount, - public LLMouseHandler -{ -public: - LLTextSegment(S32 start, S32 end) - : mStart(start), - mEnd(end) - {} - virtual ~LLTextSegment(); - bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; - - virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; - virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; - - /** - * Get number of chars that fit into free part of current line. - * - * @param num_pixels - maximum width of rect - * @param segment_offset - symbol in segment we start processing line from - * @param line_offset - symbol in line after which segment starts - * @param max_chars - limit of symbols that will fit in current line - * @param line_ind - index of not word-wrapped string inside segment for multi-line segments. - * Two string separated by word-wrap will have same index. - * @return number of chars that will fit into current line - */ - virtual S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; - virtual void updateLayout(const class LLTextBase& editor); - virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - virtual bool canEdit() const; - virtual void unlinkFromDocument(class LLTextBase* editor); - virtual void linkToDocument(class LLTextBase* editor); - - virtual const LLColor4& getColor() const; - //virtual void setColor(const LLColor4 &color); - virtual LLStyleConstSP getStyle() const; - virtual void setStyle(LLStyleConstSP style); - virtual void setToken( LLKeywordToken* token ); - virtual LLKeywordToken* getToken() const; - virtual void setToolTip(const std::string& tooltip); - virtual void dump() const; - - // LLMouseHandler interface - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMiddleMouseUp(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*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - /*virtual*/ const std::string& getName() const; - /*virtual*/ void onMouseCaptureLost(); - /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; - /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; - /*virtual*/ bool hasMouseCapture(); - - S32 getStart() const { return mStart; } - void setStart(S32 start) { mStart = start; } - S32 getEnd() const { return mEnd; } - void setEnd( S32 end ) { mEnd = end; } - -protected: - S32 mStart; - S32 mEnd; -}; - -class LLNormalTextSegment : public LLTextSegment -{ -public: - LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); - LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); - virtual ~LLNormalTextSegment(); - - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; - /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; - /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; - /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - /*virtual*/ bool canEdit() const { return true; } - /*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); } - /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; } - /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; } - /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; } - /*virtual*/ LLKeywordToken* getToken() const { return mToken; } - /*virtual*/ bool getToolTip( std::string& msg ) const; - /*virtual*/ void setToolTip(const std::string& tooltip); - /*virtual*/ void dump() const; - - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - -protected: - F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect); - - virtual const LLWString& getWText() const; - virtual const S32 getLength() const; - -protected: - class LLTextBase& mEditor; - LLStyleConstSP mStyle; - S32 mFontHeight; - LLKeywordToken* mToken; - std::string mTooltip; - boost::signals2::connection mImageLoadedConnection; -}; - -// This text segment is the same as LLNormalTextSegment, the only difference -// is that LLNormalTextSegment draws value of LLTextBase (LLTextBase::getWText()), -// but LLLabelTextSegment draws label of the LLTextBase (LLTextBase::mLabel) -class LLLabelTextSegment : public LLNormalTextSegment -{ -public: - LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); - LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); - -protected: - - /*virtual*/ const LLWString& getWText() const; - /*virtual*/ const S32 getLength() const; -}; - -// Text segment that represents a single emoji character that has a different style (=font size) than the rest of -// the document it belongs to -class LLEmojiTextSegment : public LLNormalTextSegment -{ -public: - LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor); - LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); - - bool canEdit() const override { return false; } - bool handleToolTip(S32 x, S32 y, MASK mask) override; -}; - -// Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment) -class LLOnHoverChangeableTextSegment : public LLNormalTextSegment -{ -public: - LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ); - /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); -protected: - // Style used for text when mouse pointer is over segment - LLStyleConstSP mHoveredStyle; - // Style used for text when mouse pointer is outside segment - LLStyleConstSP mNormalStyle; - -}; - -class LLIndexSegment : public LLTextSegment -{ -public: - LLIndexSegment() : LLTextSegment(0, 0) {} -}; - -class LLInlineViewSegment : public LLTextSegment -{ -public: - struct Params : public LLInitParam::Block - { - Mandatory view; - Optional force_newline; - Optional left_pad, - right_pad, - bottom_pad, - top_pad; - }; - - LLInlineViewSegment(const Params& p, S32 start, S32 end); - ~LLInlineViewSegment(); - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; - /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; - /*virtual*/ void updateLayout(const class LLTextBase& editor); - /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - /*virtual*/ bool canEdit() const { return false; } - /*virtual*/ void unlinkFromDocument(class LLTextBase* editor); - /*virtual*/ void linkToDocument(class LLTextBase* editor); - -private: - S32 mLeftPad; - S32 mRightPad; - S32 mTopPad; - S32 mBottomPad; - LLView* mView; - bool mForceNewLine; -}; - -class LLLineBreakTextSegment : public LLTextSegment -{ -public: - - LLLineBreakTextSegment(LLStyleConstSP style,S32 pos); - LLLineBreakTextSegment(S32 pos); - ~LLLineBreakTextSegment(); - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; - S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; - F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - -private: - S32 mFontHeight; -}; - -class LLImageTextSegment : public LLTextSegment -{ -public: - LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor); - ~LLImageTextSegment(); - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; - S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const; - F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - - /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - /*virtual*/ void setToolTip(const std::string& tooltip); - -private: - class LLTextBase& mEditor; - LLStyleConstSP mStyle; - -protected: - std::string mTooltip; -}; - -typedef LLPointer LLTextSegmentPtr; - -/// -/// The LLTextBase class provides a base class for all text fields, such -/// as LLTextEditor and LLTextBox. It implements shared functionality -/// such as Url highlighting and opening. -/// -class LLTextBase -: public LLUICtrl, - protected LLEditMenuHandler, - public LLSpellCheckMenuHandler, - public ll::ui::SearchableControl -{ -public: - friend class LLTextSegment; - friend class LLNormalTextSegment; - friend class LLUICtrlFactory; - - typedef boost::signals2::signal is_friend_signal_t; - typedef boost::signals2::signal is_blocked_signal_t; - - struct LineSpacingParams : public LLInitParam::ChoiceBlock - { - Alternative multiple; - Alternative pixels; - LineSpacingParams(); - }; - - struct Params : public LLInitParam::Block - { - Optional cursor_color, - text_color, - text_readonly_color, - text_tentative_color, - bg_readonly_color, - bg_writeable_color, - bg_focus_color, - text_selected_color, - bg_selected_color; - - Optional bg_visible, - border_visible, - track_end, - read_only, - skip_link_underline, - spellcheck, - allow_scroll, - plain_text, - wrap, - use_ellipses, - use_emoji, - use_color, - parse_urls, - force_urls_external, - parse_highlights, - clip, - clip_partial, - trusted_content, - always_show_icons; - - Optional v_pad, - h_pad; - - - Optional - line_spacing; - - Optional max_text_length; - - Optional font_shadow; - - Optional text_valign; - - Params(); - }; - - // LLMouseHandler interface - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks) override; - /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask) override; - - // LLView interface - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override; - /*virtual*/ void draw() override; - - // LLUICtrl interface - /*virtual*/ bool acceptsTextInput() const override { return !mReadOnly; } - /*virtual*/ void setColor(const LLColor4& c) override; - virtual void setReadOnlyColor(const LLColor4 &c); - /*virtual*/ void onVisibilityChange(bool new_visibility) override; - - /*virtual*/ void setValue(const LLSD& value) override; - /*virtual*/ LLTextViewModel* getViewModel() const override; - - // LLEditMenuHandler interface - /*virtual*/ bool canDeselect() const override; - /*virtual*/ void deselect() override; - - virtual void onFocusReceived() override; - virtual void onFocusLost() override; - - void setParseHTML(bool parse_html) { mParseHTML = parse_html; } - - // LLSpellCheckMenuHandler overrides - /*virtual*/ bool getSpellCheck() const override; - - /*virtual*/ const std::string& getSuggestion(U32 index) const override; - /*virtual*/ U32 getSuggestionCount() const override; - /*virtual*/ void replaceWithSuggestion(U32 index) override; - - /*virtual*/ void addToDictionary() override; - /*virtual*/ bool canAddToDictionary() const override; - - /*virtual*/ void addToIgnore() override; - /*virtual*/ bool canAddToIgnore() const override; - - // Spell checking helper functions - std::string getMisspelledWord(U32 pos) const; - bool isMisspelledWord(U32 pos) const; - void onSpellCheckSettingsChange(); - virtual void onSpellCheckPerformed(){} - - // used by LLTextSegment layout code - bool getWordWrap() const { return mWordWrap; } - bool getUseEllipses() const { return mUseEllipses; } - bool getUseEmoji() const { return mUseEmoji; } - void setUseEmoji(bool value) { mUseEmoji = value; } - bool getUseColor() const { return mUseColor; } - void setUseColor(bool value) { mUseColor = value; } - bool truncate(); // returns true of truncation occurred - - bool isContentTrusted() const { return mTrustedContent; } - void setContentTrusted(bool trusted_content) { mTrustedContent = trusted_content; } - - // TODO: move into LLTextSegment? - void createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url - - // Text accessors - // TODO: add optional style parameter - virtual void setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style - /*virtual*/ const std::string& getText() const override; - void setMaxTextLength(S32 length) { mMaxTextByteLength = length; } - S32 getMaxTextLength() { return mMaxTextByteLength; } - - // wide-char versions - void setWText(const LLWString& text); - const LLWString& getWText() const; - - void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); - - void setLabel(const LLStringExplicit& label); - /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override; - - const std::string& getLabel() { return mLabel.getString(); } - const LLWString& getWlabel() { return mLabel.getWString();} - - void setLastSegmentToolTip(const std::string &tooltip); - - /** - * If label is set, draws text label (which is LLLabelTextSegment) - * that is visible when no user text provided - */ - void resetLabel(); - - void setFont(const LLFontGL* font); - - // force reflow of text - void needsReflow(S32 index = 0); - - S32 getLength() const { return getWText().length(); } - S32 getLineCount() const { return mLineInfoList.size(); } - S32 removeFirstLine(); // returns removed length - - void addDocumentChild(LLView* view); - void removeDocumentChild(LLView* view); - const LLView* getDocumentView() const { return mDocumentView; } - LLRect getVisibleTextRect() const { return mVisibleTextRect; } - LLRect getTextBoundingRect(); - LLRect getVisibleDocumentRect() const; - - S32 getVPad() { return mVPad; } - S32 getHPad() { return mHPad; } - F32 getLineSpacingMult() { return mLineSpacingMult; } - S32 getLineSpacingPixels() { return mLineSpacingPixels; } // only for multiline - - S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line = true) const; - LLRect getLocalRectFromDocIndex(S32 pos) const; - LLRect getDocRectFromDocIndex(S32 pos) const; - - void setReadOnly(bool read_only) { mReadOnly = read_only; } - bool getReadOnly() { return mReadOnly; } - - void setSkipLinkUnderline(bool skip_link_underline) { mSkipLinkUnderline = skip_link_underline; } - bool getSkipLinkUnderline() { return mSkipLinkUnderline; } - - void setParseURLs(bool parse_urls) { mParseHTML = parse_urls; } - - void setPlainText(bool value) { mPlainText = value;} - bool getPlainText() const { return mPlainText; } - - // cursor manipulation - bool setCursor(S32 row, S32 column); - bool setCursorPos(S32 cursor_pos, bool keep_cursor_offset = false); - void startOfLine(); - void endOfLine(); - void startOfDoc(); - void endOfDoc(); - void changePage( S32 delta ); - void changeLine( S32 delta ); - - bool scrolledToStart(); - bool scrolledToEnd(); - - const LLFontGL* getFont() const override { return mFont; } - - virtual void appendLineBreakSegment(const LLStyle::Params& style_params); - virtual void appendImageSegment(const LLStyle::Params& style_params); - virtual void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo); - boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb); - boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb); - boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb); - - void setWordWrap(bool wrap); - LLScrollContainer* getScrollContainer() const { return mScroller; } - -protected: - // protected member variables - // List of offsets and segment index of the start of each line. Always has at least one node (0). - struct line_info - { - line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num); - S32 mDocIndexStart; - S32 mDocIndexEnd; - LLRect mRect; - S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap) - }; - typedef std::vector line_list_t; - - // helper structs - struct compare_bottom - { - bool operator()(const S32& a, const line_info& b) const; - bool operator()(const line_info& a, const S32& b) const; - bool operator()(const line_info& a, const line_info& b) const; - }; - struct compare_top - { - bool operator()(const S32& a, const line_info& b) const; - bool operator()(const line_info& a, const S32& b) const; - bool operator()(const line_info& a, const line_info& b) const; - }; - struct line_end_compare; - typedef std::vector segment_vec_t; - - // Abstract inner base class representing an undoable editor command. - // Concrete sub-classes can be defined for operations such as insert, remove, etc. - // Used as arguments to the execute() method below. - class TextCmd - { - public: - TextCmd( S32 pos, bool group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() ) - : mPos(pos), - mGroupWithNext(group_with_next) - { - if (segment.notNull()) - { - mSegments.push_back(segment); - } - } - virtual ~TextCmd() {} - virtual bool execute(LLTextBase* editor, S32* delta) = 0; - virtual S32 undo(LLTextBase* editor) = 0; - virtual S32 redo(LLTextBase* editor) = 0; - virtual bool canExtend(S32 pos) const { return false; } - virtual void blockExtensions() {} - virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; } - virtual bool hasExtCharValue( llwchar value ) const { return false; } - - // Defined here so they can access protected LLTextEditor editing methods - S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); } - S32 remove(LLTextBase* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); } - S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); } - - S32 getPosition() const { return mPos; } - bool groupWithNext() const { return mGroupWithNext; } - - protected: - const S32 mPos; - bool mGroupWithNext; - segment_vec_t mSegments; - }; - - struct compare_segment_end - { - bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const; - }; - typedef std::multiset segment_set_t; - - // member functions - LLTextBase(const Params &p); - virtual ~LLTextBase(); - void initFromParams(const Params& p); - virtual void beforeValueChange(); - virtual void onValueChange(S32 start, S32 end); - virtual bool useLabel() const; - - // draw methods - virtual void drawSelectionBackground(); // draws the black box behind the selected text - void drawCursor(); - void drawText(); - - // modify contents - S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted - S32 removeStringNoUndo(S32 pos, S32 length); - S32 overwriteCharNoUndo(S32 pos, llwchar wc); - void appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& stylep, bool underline_on_hover_only = false); - - - // manage segments - void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const; - void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ); - LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true); - segment_set_t::iterator getEditableSegIterContaining(S32 index); - segment_set_t::const_iterator getEditableSegIterContaining(S32 index) const; - segment_set_t::iterator getSegIterContaining(S32 index); - segment_set_t::const_iterator getSegIterContaining(S32 index) const; - void clearSegments(); - void createDefaultSegment(); - virtual void updateSegments(); - void insertSegment(LLTextSegmentPtr segment_to_insert); - const LLStyle::Params& getStyleParams(); - - // manage lines - S32 getLineStart( S32 line ) const; - S32 getLineEnd( S32 line ) const; - S32 getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap = true) const; - S32 getLineOffsetFromDocIndex( S32 doc_index, bool include_wordwrap = true) const; - S32 getFirstVisibleLine() const; - std::pair getVisibleLines(bool fully_visible = false); - S32 getLeftOffset(S32 width); - void reflow(); - - // cursor - void updateCursorXPos(); - void setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset=false ); - S32 getEditableIndex(S32 index, bool increasing_direction); // constraint cursor to editable segments of document - void resetCursorBlink() { mCursorBlinkTimer.reset(); } - void updateScrollFromCursor(); - - // text selection - bool hasSelection() const { return (mSelectionStart !=mSelectionEnd); } - void startSelection(); - void endSelection(); - - // misc - void updateRects(); - void needsScroll() { mScrollNeeded = true; } - - struct URLLabelCallback; - // Replace a URL with a new icon and label, for example, when - // avatar names are looked up. - void replaceUrl(const std::string &url, const std::string &label, const std::string& icon); - - void appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params()); - void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only = false); - S32 normalizeUri(std::string& uri); - -protected: - // virtual - std::string _getSearchText() const override - { - return mLabel.getString() + getToolTip(); - } - - std::vector getSelectionRects(); - -protected: - // text segmentation and flow - segment_set_t mSegments; - line_list_t mLineInfoList; - LLRect mVisibleTextRect; // The rect in which text is drawn. Excludes borders. - LLRect mTextBoundingRect; - - // default text style - LLStyle::Params mStyle; - bool mStyleDirty; - const LLFontGL* mFont; - const LLFontGL::ShadowType mFontShadow; - - // colors - LLUIColor mCursorColor; - LLUIColor mFgColor; - LLUIColor mReadOnlyFgColor; - LLUIColor mTentativeFgColor; - LLUIColor mWriteableBgColor; - LLUIColor mReadOnlyBgColor; - LLUIColor mFocusBgColor; - LLUIColor mTextSelectedColor; - LLUIColor mSelectedBGColor; - - // cursor - S32 mCursorPos; // I-beam is just after the mCursorPos-th character. - S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be - LLFrameTimer mCursorBlinkTimer; // timer that controls cursor blinking - - // selection - S32 mSelectionStart; - S32 mSelectionEnd; - LLTimer mTripleClickTimer; - - bool mIsSelecting; // Are we in the middle of a drag-select? - - // spell checking - bool mSpellCheck; - S32 mSpellCheckStart; - S32 mSpellCheckEnd; - LLTimer mSpellCheckTimer; - std::list > mMisspellRanges; - std::vector mSuggestionList; - - // configuration - S32 mHPad; // padding on left of text - S32 mVPad; // padding above text - LLFontGL::HAlign mHAlign; // horizontal alignment of the document in its entirety - LLFontGL::VAlign mVAlign; // vertical alignment of the document in its entirety - LLFontGL::VAlign mTextVAlign; // vertical alignment of a text segment within a single line of text - F32 mLineSpacingMult; // multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding) - S32 mLineSpacingPixels; // padding between lines - bool mBorderVisible; - bool mParseHTML; // make URLs interactive - bool mForceUrlsExternal; // URLs from this textbox will be opened in external browser - bool mParseHighlights; // highlight user-defined keywords - bool mWordWrap; - bool mUseEllipses; - bool mUseEmoji; - bool mUseColor; - bool mTrackEnd; // if true, keeps scroll position at end of document during resize - bool mReadOnly; - bool mBGVisible; // render background? - bool mClip; // clip text to widget rect - bool mClipPartial; // false if we show lines that are partially inside bounding rect - bool mTrustedContent; // if false, does not allow to execute SURL links from this editor - bool mPlainText; // didn't use Image or Icon segments - bool mAutoIndent; - S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes - bool mSkipTripleClick; - bool mAlwaysShowIcons; - - bool mSkipLinkUnderline; - - // support widgets - LLHandle mPopupMenuHandle; - LLView* mDocumentView; - LLScrollContainer* mScroller; - - // transient state - S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed. - bool mScrollNeeded; // need to change scroll region because of change to cursor position - S32 mScrollIndex; // index of first character to keep visible in scroll region - - // Fired when a URL link is clicked - commit_signal_t* mURLClickSignal; - - // Used to check if user with given ID is avatar's friend - is_friend_signal_t* mIsFriendSignal; - is_blocked_signal_t* mIsObjectBlockedSignal; - - LLUIString mLabel; // text label that is visible when no user text provided -}; - -#endif +/** + * @file lltextbase.h + * @author Martin Reddy + * @brief The base class of text box/editor, providing Url handling support + * + * $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_LLTEXTBASE_H +#define LL_LLTEXTBASE_H + +#include "v4color.h" +#include "lleditmenuhandler.h" +#include "llspellcheckmenuhandler.h" +#include "llstyle.h" +#include "llkeywords.h" +#include "llpanel.h" + +#include +#include +#include + +#include + +class LLScrollContainer; +class LLContextMenu; +class LLUrlMatch; + +/// +/// A text segment is used to specify a subsection of a text string +/// that should be formatted differently, such as a hyperlink. It +/// includes a start/end offset from the start of the string, a +/// style to render with, an optional tooltip, etc. +/// +class LLTextSegment +: public LLRefCount, + public LLMouseHandler +{ +public: + LLTextSegment(S32 start, S32 end) + : mStart(start), + mEnd(end) + {} + virtual ~LLTextSegment(); + bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + + virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; + + /** + * Get number of chars that fit into free part of current line. + * + * @param num_pixels - maximum width of rect + * @param segment_offset - symbol in segment we start processing line from + * @param line_offset - symbol in line after which segment starts + * @param max_chars - limit of symbols that will fit in current line + * @param line_ind - index of not word-wrapped string inside segment for multi-line segments. + * Two string separated by word-wrap will have same index. + * @return number of chars that will fit into current line + */ + virtual S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; + virtual void updateLayout(const class LLTextBase& editor); + virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); + virtual bool canEdit() const; + virtual void unlinkFromDocument(class LLTextBase* editor); + virtual void linkToDocument(class LLTextBase* editor); + + virtual const LLColor4& getColor() const; + //virtual void setColor(const LLColor4 &color); + virtual LLStyleConstSP getStyle() const; + virtual void setStyle(LLStyleConstSP style); + virtual void setToken( LLKeywordToken* token ); + virtual LLKeywordToken* getToken() const; + virtual void setToolTip(const std::string& tooltip); + virtual void dump() const; + + // LLMouseHandler interface + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseUp(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*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ const std::string& getName() const; + /*virtual*/ void onMouseCaptureLost(); + /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; + /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; + /*virtual*/ bool hasMouseCapture(); + + S32 getStart() const { return mStart; } + void setStart(S32 start) { mStart = start; } + S32 getEnd() const { return mEnd; } + void setEnd( S32 end ) { mEnd = end; } + +protected: + S32 mStart; + S32 mEnd; +}; + +class LLNormalTextSegment : public LLTextSegment +{ +public: + LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); + LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); + virtual ~LLNormalTextSegment(); + + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; + /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); + /*virtual*/ bool canEdit() const { return true; } + /*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); } + /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; } + /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; } + /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; } + /*virtual*/ LLKeywordToken* getToken() const { return mToken; } + /*virtual*/ bool getToolTip( std::string& msg ) const; + /*virtual*/ void setToolTip(const std::string& tooltip); + /*virtual*/ void dump() const; + + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); + +protected: + F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect); + + virtual const LLWString& getWText() const; + virtual const S32 getLength() const; + +protected: + class LLTextBase& mEditor; + LLStyleConstSP mStyle; + S32 mFontHeight; + LLKeywordToken* mToken; + std::string mTooltip; + boost::signals2::connection mImageLoadedConnection; +}; + +// This text segment is the same as LLNormalTextSegment, the only difference +// is that LLNormalTextSegment draws value of LLTextBase (LLTextBase::getWText()), +// but LLLabelTextSegment draws label of the LLTextBase (LLTextBase::mLabel) +class LLLabelTextSegment : public LLNormalTextSegment +{ +public: + LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); + LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); + +protected: + + /*virtual*/ const LLWString& getWText() const; + /*virtual*/ const S32 getLength() const; +}; + +// Text segment that represents a single emoji character that has a different style (=font size) than the rest of +// the document it belongs to +class LLEmojiTextSegment : public LLNormalTextSegment +{ +public: + LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor); + LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); + + bool canEdit() const override { return false; } + bool handleToolTip(S32 x, S32 y, MASK mask) override; +}; + +// Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment) +class LLOnHoverChangeableTextSegment : public LLNormalTextSegment +{ +public: + LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ); + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); +protected: + // Style used for text when mouse pointer is over segment + LLStyleConstSP mHoveredStyle; + // Style used for text when mouse pointer is outside segment + LLStyleConstSP mNormalStyle; + +}; + +class LLIndexSegment : public LLTextSegment +{ +public: + LLIndexSegment() : LLTextSegment(0, 0) {} +}; + +class LLInlineViewSegment : public LLTextSegment +{ +public: + struct Params : public LLInitParam::Block + { + Mandatory view; + Optional force_newline; + Optional left_pad, + right_pad, + bottom_pad, + top_pad; + }; + + LLInlineViewSegment(const Params& p, S32 start, S32 end); + ~LLInlineViewSegment(); + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; + /*virtual*/ void updateLayout(const class LLTextBase& editor); + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); + /*virtual*/ bool canEdit() const { return false; } + /*virtual*/ void unlinkFromDocument(class LLTextBase* editor); + /*virtual*/ void linkToDocument(class LLTextBase* editor); + +private: + S32 mLeftPad; + S32 mRightPad; + S32 mTopPad; + S32 mBottomPad; + LLView* mView; + bool mForceNewLine; +}; + +class LLLineBreakTextSegment : public LLTextSegment +{ +public: + + LLLineBreakTextSegment(LLStyleConstSP style,S32 pos); + LLLineBreakTextSegment(S32 pos); + ~LLLineBreakTextSegment(); + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; + F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); + +private: + S32 mFontHeight; +}; + +class LLImageTextSegment : public LLTextSegment +{ +public: + LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor); + ~LLImageTextSegment(); + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const; + F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); + + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ void setToolTip(const std::string& tooltip); + +private: + class LLTextBase& mEditor; + LLStyleConstSP mStyle; + +protected: + std::string mTooltip; +}; + +typedef LLPointer LLTextSegmentPtr; + +/// +/// The LLTextBase class provides a base class for all text fields, such +/// as LLTextEditor and LLTextBox. It implements shared functionality +/// such as Url highlighting and opening. +/// +class LLTextBase +: public LLUICtrl, + protected LLEditMenuHandler, + public LLSpellCheckMenuHandler, + public ll::ui::SearchableControl +{ +public: + friend class LLTextSegment; + friend class LLNormalTextSegment; + friend class LLUICtrlFactory; + + typedef boost::signals2::signal is_friend_signal_t; + typedef boost::signals2::signal is_blocked_signal_t; + + struct LineSpacingParams : public LLInitParam::ChoiceBlock + { + Alternative multiple; + Alternative pixels; + LineSpacingParams(); + }; + + struct Params : public LLInitParam::Block + { + Optional cursor_color, + text_color, + text_readonly_color, + text_tentative_color, + bg_readonly_color, + bg_writeable_color, + bg_focus_color, + text_selected_color, + bg_selected_color; + + Optional bg_visible, + border_visible, + track_end, + read_only, + skip_link_underline, + spellcheck, + allow_scroll, + plain_text, + wrap, + use_ellipses, + use_emoji, + use_color, + parse_urls, + force_urls_external, + parse_highlights, + clip, + clip_partial, + trusted_content, + always_show_icons; + + Optional v_pad, + h_pad; + + + Optional + line_spacing; + + Optional max_text_length; + + Optional font_shadow; + + Optional text_valign; + + Params(); + }; + + // LLMouseHandler interface + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks) override; + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask) override; + + // LLView interface + /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override; + /*virtual*/ void draw() override; + + // LLUICtrl interface + /*virtual*/ bool acceptsTextInput() const override { return !mReadOnly; } + /*virtual*/ void setColor(const LLColor4& c) override; + virtual void setReadOnlyColor(const LLColor4 &c); + /*virtual*/ void onVisibilityChange(bool new_visibility) override; + + /*virtual*/ void setValue(const LLSD& value) override; + /*virtual*/ LLTextViewModel* getViewModel() const override; + + // LLEditMenuHandler interface + /*virtual*/ bool canDeselect() const override; + /*virtual*/ void deselect() override; + + virtual void onFocusReceived() override; + virtual void onFocusLost() override; + + void setParseHTML(bool parse_html) { mParseHTML = parse_html; } + + // LLSpellCheckMenuHandler overrides + /*virtual*/ bool getSpellCheck() const override; + + /*virtual*/ const std::string& getSuggestion(U32 index) const override; + /*virtual*/ U32 getSuggestionCount() const override; + /*virtual*/ void replaceWithSuggestion(U32 index) override; + + /*virtual*/ void addToDictionary() override; + /*virtual*/ bool canAddToDictionary() const override; + + /*virtual*/ void addToIgnore() override; + /*virtual*/ bool canAddToIgnore() const override; + + // Spell checking helper functions + std::string getMisspelledWord(U32 pos) const; + bool isMisspelledWord(U32 pos) const; + void onSpellCheckSettingsChange(); + virtual void onSpellCheckPerformed(){} + + // used by LLTextSegment layout code + bool getWordWrap() const { return mWordWrap; } + bool getUseEllipses() const { return mUseEllipses; } + bool getUseEmoji() const { return mUseEmoji; } + void setUseEmoji(bool value) { mUseEmoji = value; } + bool getUseColor() const { return mUseColor; } + void setUseColor(bool value) { mUseColor = value; } + bool truncate(); // returns true of truncation occurred + + bool isContentTrusted() const { return mTrustedContent; } + void setContentTrusted(bool trusted_content) { mTrustedContent = trusted_content; } + + // TODO: move into LLTextSegment? + void createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url + + // Text accessors + // TODO: add optional style parameter + virtual void setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style + /*virtual*/ const std::string& getText() const override; + void setMaxTextLength(S32 length) { mMaxTextByteLength = length; } + S32 getMaxTextLength() { return mMaxTextByteLength; } + + // wide-char versions + void setWText(const LLWString& text); + const LLWString& getWText() const; + + void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); + + void setLabel(const LLStringExplicit& label); + /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override; + + const std::string& getLabel() { return mLabel.getString(); } + const LLWString& getWlabel() { return mLabel.getWString();} + + void setLastSegmentToolTip(const std::string &tooltip); + + /** + * If label is set, draws text label (which is LLLabelTextSegment) + * that is visible when no user text provided + */ + void resetLabel(); + + void setFont(const LLFontGL* font); + + // force reflow of text + void needsReflow(S32 index = 0); + + S32 getLength() const { return getWText().length(); } + S32 getLineCount() const { return mLineInfoList.size(); } + S32 removeFirstLine(); // returns removed length + + void addDocumentChild(LLView* view); + void removeDocumentChild(LLView* view); + const LLView* getDocumentView() const { return mDocumentView; } + LLRect getVisibleTextRect() const { return mVisibleTextRect; } + LLRect getTextBoundingRect(); + LLRect getVisibleDocumentRect() const; + + S32 getVPad() { return mVPad; } + S32 getHPad() { return mHPad; } + F32 getLineSpacingMult() { return mLineSpacingMult; } + S32 getLineSpacingPixels() { return mLineSpacingPixels; } // only for multiline + + S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line = true) const; + LLRect getLocalRectFromDocIndex(S32 pos) const; + LLRect getDocRectFromDocIndex(S32 pos) const; + + void setReadOnly(bool read_only) { mReadOnly = read_only; } + bool getReadOnly() { return mReadOnly; } + + void setSkipLinkUnderline(bool skip_link_underline) { mSkipLinkUnderline = skip_link_underline; } + bool getSkipLinkUnderline() { return mSkipLinkUnderline; } + + void setParseURLs(bool parse_urls) { mParseHTML = parse_urls; } + + void setPlainText(bool value) { mPlainText = value;} + bool getPlainText() const { return mPlainText; } + + // cursor manipulation + bool setCursor(S32 row, S32 column); + bool setCursorPos(S32 cursor_pos, bool keep_cursor_offset = false); + void startOfLine(); + void endOfLine(); + void startOfDoc(); + void endOfDoc(); + void changePage( S32 delta ); + void changeLine( S32 delta ); + + bool scrolledToStart(); + bool scrolledToEnd(); + + const LLFontGL* getFont() const override { return mFont; } + + virtual void appendLineBreakSegment(const LLStyle::Params& style_params); + virtual void appendImageSegment(const LLStyle::Params& style_params); + virtual void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo); + boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb); + boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb); + boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb); + + void setWordWrap(bool wrap); + LLScrollContainer* getScrollContainer() const { return mScroller; } + +protected: + // protected member variables + // List of offsets and segment index of the start of each line. Always has at least one node (0). + struct line_info + { + line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num); + S32 mDocIndexStart; + S32 mDocIndexEnd; + LLRect mRect; + S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap) + }; + typedef std::vector line_list_t; + + // helper structs + struct compare_bottom + { + bool operator()(const S32& a, const line_info& b) const; + bool operator()(const line_info& a, const S32& b) const; + bool operator()(const line_info& a, const line_info& b) const; + }; + struct compare_top + { + bool operator()(const S32& a, const line_info& b) const; + bool operator()(const line_info& a, const S32& b) const; + bool operator()(const line_info& a, const line_info& b) const; + }; + struct line_end_compare; + typedef std::vector segment_vec_t; + + // Abstract inner base class representing an undoable editor command. + // Concrete sub-classes can be defined for operations such as insert, remove, etc. + // Used as arguments to the execute() method below. + class TextCmd + { + public: + TextCmd( S32 pos, bool group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() ) + : mPos(pos), + mGroupWithNext(group_with_next) + { + if (segment.notNull()) + { + mSegments.push_back(segment); + } + } + virtual ~TextCmd() {} + virtual bool execute(LLTextBase* editor, S32* delta) = 0; + virtual S32 undo(LLTextBase* editor) = 0; + virtual S32 redo(LLTextBase* editor) = 0; + virtual bool canExtend(S32 pos) const { return false; } + virtual void blockExtensions() {} + virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; } + virtual bool hasExtCharValue( llwchar value ) const { return false; } + + // Defined here so they can access protected LLTextEditor editing methods + S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); } + S32 remove(LLTextBase* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); } + S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); } + + S32 getPosition() const { return mPos; } + bool groupWithNext() const { return mGroupWithNext; } + + protected: + const S32 mPos; + bool mGroupWithNext; + segment_vec_t mSegments; + }; + + struct compare_segment_end + { + bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const; + }; + typedef std::multiset segment_set_t; + + // member functions + LLTextBase(const Params &p); + virtual ~LLTextBase(); + void initFromParams(const Params& p); + virtual void beforeValueChange(); + virtual void onValueChange(S32 start, S32 end); + virtual bool useLabel() const; + + // draw methods + virtual void drawSelectionBackground(); // draws the black box behind the selected text + void drawCursor(); + void drawText(); + + // modify contents + S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted + S32 removeStringNoUndo(S32 pos, S32 length); + S32 overwriteCharNoUndo(S32 pos, llwchar wc); + void appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& stylep, bool underline_on_hover_only = false); + + + // manage segments + void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const; + void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ); + LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true); + segment_set_t::iterator getEditableSegIterContaining(S32 index); + segment_set_t::const_iterator getEditableSegIterContaining(S32 index) const; + segment_set_t::iterator getSegIterContaining(S32 index); + segment_set_t::const_iterator getSegIterContaining(S32 index) const; + void clearSegments(); + void createDefaultSegment(); + virtual void updateSegments(); + void insertSegment(LLTextSegmentPtr segment_to_insert); + const LLStyle::Params& getStyleParams(); + + // manage lines + S32 getLineStart( S32 line ) const; + S32 getLineEnd( S32 line ) const; + S32 getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap = true) const; + S32 getLineOffsetFromDocIndex( S32 doc_index, bool include_wordwrap = true) const; + S32 getFirstVisibleLine() const; + std::pair getVisibleLines(bool fully_visible = false); + S32 getLeftOffset(S32 width); + void reflow(); + + // cursor + void updateCursorXPos(); + void setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset=false ); + S32 getEditableIndex(S32 index, bool increasing_direction); // constraint cursor to editable segments of document + void resetCursorBlink() { mCursorBlinkTimer.reset(); } + void updateScrollFromCursor(); + + // text selection + bool hasSelection() const { return (mSelectionStart !=mSelectionEnd); } + void startSelection(); + void endSelection(); + + // misc + void updateRects(); + void needsScroll() { mScrollNeeded = true; } + + struct URLLabelCallback; + // Replace a URL with a new icon and label, for example, when + // avatar names are looked up. + void replaceUrl(const std::string &url, const std::string &label, const std::string& icon); + + void appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params()); + void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only = false); + S32 normalizeUri(std::string& uri); + +protected: + // virtual + std::string _getSearchText() const override + { + return mLabel.getString() + getToolTip(); + } + + std::vector getSelectionRects(); + +protected: + // text segmentation and flow + segment_set_t mSegments; + line_list_t mLineInfoList; + LLRect mVisibleTextRect; // The rect in which text is drawn. Excludes borders. + LLRect mTextBoundingRect; + + // default text style + LLStyle::Params mStyle; + bool mStyleDirty; + const LLFontGL* mFont; + const LLFontGL::ShadowType mFontShadow; + + // colors + LLUIColor mCursorColor; + LLUIColor mFgColor; + LLUIColor mReadOnlyFgColor; + LLUIColor mTentativeFgColor; + LLUIColor mWriteableBgColor; + LLUIColor mReadOnlyBgColor; + LLUIColor mFocusBgColor; + LLUIColor mTextSelectedColor; + LLUIColor mSelectedBGColor; + + // cursor + S32 mCursorPos; // I-beam is just after the mCursorPos-th character. + S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be + LLFrameTimer mCursorBlinkTimer; // timer that controls cursor blinking + + // selection + S32 mSelectionStart; + S32 mSelectionEnd; + LLTimer mTripleClickTimer; + + bool mIsSelecting; // Are we in the middle of a drag-select? + + // spell checking + bool mSpellCheck; + S32 mSpellCheckStart; + S32 mSpellCheckEnd; + LLTimer mSpellCheckTimer; + std::list > mMisspellRanges; + std::vector mSuggestionList; + + // configuration + S32 mHPad; // padding on left of text + S32 mVPad; // padding above text + LLFontGL::HAlign mHAlign; // horizontal alignment of the document in its entirety + LLFontGL::VAlign mVAlign; // vertical alignment of the document in its entirety + LLFontGL::VAlign mTextVAlign; // vertical alignment of a text segment within a single line of text + F32 mLineSpacingMult; // multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding) + S32 mLineSpacingPixels; // padding between lines + bool mBorderVisible; + bool mParseHTML; // make URLs interactive + bool mForceUrlsExternal; // URLs from this textbox will be opened in external browser + bool mParseHighlights; // highlight user-defined keywords + bool mWordWrap; + bool mUseEllipses; + bool mUseEmoji; + bool mUseColor; + bool mTrackEnd; // if true, keeps scroll position at end of document during resize + bool mReadOnly; + bool mBGVisible; // render background? + bool mClip; // clip text to widget rect + bool mClipPartial; // false if we show lines that are partially inside bounding rect + bool mTrustedContent; // if false, does not allow to execute SURL links from this editor + bool mPlainText; // didn't use Image or Icon segments + bool mAutoIndent; + S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes + bool mSkipTripleClick; + bool mAlwaysShowIcons; + + bool mSkipLinkUnderline; + + // support widgets + LLHandle mPopupMenuHandle; + LLView* mDocumentView; + LLScrollContainer* mScroller; + + // transient state + S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed. + bool mScrollNeeded; // need to change scroll region because of change to cursor position + S32 mScrollIndex; // index of first character to keep visible in scroll region + + // Fired when a URL link is clicked + commit_signal_t* mURLClickSignal; + + // Used to check if user with given ID is avatar's friend + is_friend_signal_t* mIsFriendSignal; + is_blocked_signal_t* mIsObjectBlockedSignal; + + LLUIString mLabel; // text label that is visible when no user text provided +}; + +#endif diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index b3b97c00b4..92551b682c 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -1,183 +1,183 @@ -/** - * @file lltextbox.cpp - * @brief A text display widget - * - * $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 "linden_common.h" - -#define LLTEXTBOX_CPP -#include "lltextbox.h" - -#include "lluictrlfactory.h" -#include "llfocusmgr.h" -#include "llwindow.h" -#include "llurlregistry.h" -#include "llstyle.h" - -static LLDefaultChildRegistry::Register r("text"); - -// Compiler optimization, generate extern template -template class LLTextBox* LLView::getChild( - const std::string& name, bool recurse) const; - -LLTextBox::LLTextBox(const LLTextBox::Params& p) -: LLTextBase(p), - mClickedCallback(NULL), - mShowCursorHand(true) -{ - mSkipTripleClick = true; -} - -LLTextBox::~LLTextBox() -{} - -bool LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = LLTextBase::handleMouseDown(x, y, mask); - - if (getSoundFlags() & MOUSE_DOWN) - { - make_ui_sound("UISndClick"); - } - - if (!handled && mClickedCallback) - { - handled = true; - } - - if (handled) - { - // Route future Mouse messages here preemptively. (Release on mouse up.) - gFocusMgr.setMouseCapture( this ); - } - - return handled; -} - -bool LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = LLTextBase::handleMouseUp(x, y, mask); - - if (getSoundFlags() & MOUSE_UP) - { - make_ui_sound("UISndClickRelease"); - } - - // We only handle the click if the click both started and ended within us - if (hasMouseCapture()) - { - // Release the mouse - gFocusMgr.setMouseCapture( NULL ); - - // 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 - if (mClickedCallback && !handled) - { - mClickedCallback(); - handled = true; - } - } - - return handled; -} - -bool LLTextBox::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = LLTextBase::handleHover(x, y, mask); - if (!handled && mClickedCallback && mShowCursorHand) - { - // Clickable text boxes change the cursor to a hand - LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND); - return true; - } - return handled; -} - -void LLTextBox::setEnabled(bool enabled) -{ - // just treat enabled as read-only flag - bool read_only = !enabled; - if (read_only != mReadOnly) - { - LLTextBase::setReadOnly(read_only); - updateSegments(); - } - LLTextBase::setEnabled(enabled); -} - -void LLTextBox::setText(const LLStringExplicit& text , const LLStyle::Params& input_params ) -{ - // does string argument insertion - mText.assign(text); - - LLTextBase::setText(mText.getString(), input_params ); -} - -void LLTextBox::setClickedCallback( boost::function cb, void* userdata /*= NULL */ ) -{ - mClickedCallback = boost::bind(cb, userdata); -} - -S32 LLTextBox::getTextPixelWidth() -{ - return getTextBoundingRect().getWidth(); -} - -S32 LLTextBox::getTextPixelHeight() -{ - return getTextBoundingRect().getHeight(); -} - - -LLSD LLTextBox::getValue() const -{ - return getViewModel()->getValue(); -} - -bool LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text ) -{ - mText.setArg(key, text); - LLTextBase::setText(mText.getString()); - - return true; -} - - -void LLTextBox::reshapeToFitText(bool called_from_parent) -{ - reflow(); - - S32 width = getTextPixelWidth(); - S32 height = getTextPixelHeight(); - //consider investigating reflow() to find missing width pixel (see SL-17045 changes) - reshape( width + 2 * mHPad + 1, height + 2 * mVPad, called_from_parent ); -} - - -void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label) -{ - needsReflow(); -} - +/** + * @file lltextbox.cpp + * @brief A text display widget + * + * $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 "linden_common.h" + +#define LLTEXTBOX_CPP +#include "lltextbox.h" + +#include "lluictrlfactory.h" +#include "llfocusmgr.h" +#include "llwindow.h" +#include "llurlregistry.h" +#include "llstyle.h" + +static LLDefaultChildRegistry::Register r("text"); + +// Compiler optimization, generate extern template +template class LLTextBox* LLView::getChild( + const std::string& name, bool recurse) const; + +LLTextBox::LLTextBox(const LLTextBox::Params& p) +: LLTextBase(p), + mClickedCallback(NULL), + mShowCursorHand(true) +{ + mSkipTripleClick = true; +} + +LLTextBox::~LLTextBox() +{} + +bool LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = LLTextBase::handleMouseDown(x, y, mask); + + if (getSoundFlags() & MOUSE_DOWN) + { + make_ui_sound("UISndClick"); + } + + if (!handled && mClickedCallback) + { + handled = true; + } + + if (handled) + { + // Route future Mouse messages here preemptively. (Release on mouse up.) + gFocusMgr.setMouseCapture( this ); + } + + return handled; +} + +bool LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = LLTextBase::handleMouseUp(x, y, mask); + + if (getSoundFlags() & MOUSE_UP) + { + make_ui_sound("UISndClickRelease"); + } + + // We only handle the click if the click both started and ended within us + if (hasMouseCapture()) + { + // Release the mouse + gFocusMgr.setMouseCapture( NULL ); + + // 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 + if (mClickedCallback && !handled) + { + mClickedCallback(); + handled = true; + } + } + + return handled; +} + +bool LLTextBox::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = LLTextBase::handleHover(x, y, mask); + if (!handled && mClickedCallback && mShowCursorHand) + { + // Clickable text boxes change the cursor to a hand + LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND); + return true; + } + return handled; +} + +void LLTextBox::setEnabled(bool enabled) +{ + // just treat enabled as read-only flag + bool read_only = !enabled; + if (read_only != mReadOnly) + { + LLTextBase::setReadOnly(read_only); + updateSegments(); + } + LLTextBase::setEnabled(enabled); +} + +void LLTextBox::setText(const LLStringExplicit& text , const LLStyle::Params& input_params ) +{ + // does string argument insertion + mText.assign(text); + + LLTextBase::setText(mText.getString(), input_params ); +} + +void LLTextBox::setClickedCallback( boost::function cb, void* userdata /*= NULL */ ) +{ + mClickedCallback = boost::bind(cb, userdata); +} + +S32 LLTextBox::getTextPixelWidth() +{ + return getTextBoundingRect().getWidth(); +} + +S32 LLTextBox::getTextPixelHeight() +{ + return getTextBoundingRect().getHeight(); +} + + +LLSD LLTextBox::getValue() const +{ + return getViewModel()->getValue(); +} + +bool LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text ) +{ + mText.setArg(key, text); + LLTextBase::setText(mText.getString()); + + return true; +} + + +void LLTextBox::reshapeToFitText(bool called_from_parent) +{ + reflow(); + + S32 width = getTextPixelWidth(); + S32 height = getTextPixelHeight(); + //consider investigating reflow() to find missing width pixel (see SL-17045 changes) + reshape( width + 2 * mHPad + 1, height + 2 * mVPad, called_from_parent ); +} + + +void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label) +{ + needsReflow(); +} + diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index 1e58fe954c..c1f829c2b9 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -1,87 +1,87 @@ -/** - * @file lltextbox.h - * @brief A single text item display - * - * $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$ - */ - -#ifndef LL_LLTEXTBOX_H -#define LL_LLTEXTBOX_H - -#include "lluistring.h" -#include "lltextbase.h" - -class LLTextBox : - public LLTextBase -{ -public: - - // *TODO: Add callback to Params - typedef boost::function callback_t; - - struct Params : public LLInitParam::Block - {}; - -protected: - LLTextBox(const Params&); - friend class LLUICtrlFactory; - -public: - virtual ~LLTextBox(); - - /*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*/ void setEnabled(bool enabled); - - /*virtual*/ void setText( const LLStringExplicit& text, const LLStyle::Params& input_params = LLStyle::Params() ); - - void setRightAlign() { mHAlign = LLFontGL::RIGHT; } - void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } - void setClickedCallback( boost::function cb, void* userdata = NULL ); - - void reshapeToFitText(bool called_from_parent = false); - - S32 getTextPixelWidth(); - S32 getTextPixelHeight(); - - /*virtual*/ LLSD getValue() const; - /*virtual*/ bool setTextArg( const std::string& key, const LLStringExplicit& text ); - - void setShowCursorHand(bool show_cursor) { mShowCursorHand = show_cursor; } - -protected: - void onUrlLabelUpdated(const std::string &url, const std::string &label); - - LLUIString mText; - callback_t mClickedCallback; - bool mShowCursorHand; -}; - -// Build time optimization, generate once in .cpp file -#ifndef LLTEXTBOX_CPP -extern template class LLTextBox* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -#endif +/** + * @file lltextbox.h + * @brief A single text item display + * + * $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$ + */ + +#ifndef LL_LLTEXTBOX_H +#define LL_LLTEXTBOX_H + +#include "lluistring.h" +#include "lltextbase.h" + +class LLTextBox : + public LLTextBase +{ +public: + + // *TODO: Add callback to Params + typedef boost::function callback_t; + + struct Params : public LLInitParam::Block + {}; + +protected: + LLTextBox(const Params&); + friend class LLUICtrlFactory; + +public: + virtual ~LLTextBox(); + + /*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*/ void setEnabled(bool enabled); + + /*virtual*/ void setText( const LLStringExplicit& text, const LLStyle::Params& input_params = LLStyle::Params() ); + + void setRightAlign() { mHAlign = LLFontGL::RIGHT; } + void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } + void setClickedCallback( boost::function cb, void* userdata = NULL ); + + void reshapeToFitText(bool called_from_parent = false); + + S32 getTextPixelWidth(); + S32 getTextPixelHeight(); + + /*virtual*/ LLSD getValue() const; + /*virtual*/ bool setTextArg( const std::string& key, const LLStringExplicit& text ); + + void setShowCursorHand(bool show_cursor) { mShowCursorHand = show_cursor; } + +protected: + void onUrlLabelUpdated(const std::string &url, const std::string &label); + + LLUIString mText; + callback_t mClickedCallback; + bool mShowCursorHand; +}; + +// Build time optimization, generate once in .cpp file +#ifndef LLTEXTBOX_CPP +extern template class LLTextBox* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +#endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index daffd58dfe..b4254524ad 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1,3070 +1,3070 @@ -/** - * @file lltexteditor.cpp - * - * $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$ - */ - -// Text editor widget to let users enter a a multi-line ASCII document. - -#include "linden_common.h" - -#define LLTEXTEDITOR_CPP -#include "lltexteditor.h" - -#include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR -#include "llfontgl.h" -#include "llgl.h" // LLGLSUIDefault() -#include "lllocalcliprect.h" -#include "llrender.h" -#include "llui.h" -#include "lluictrlfactory.h" -#include "llrect.h" -#include "llfocusmgr.h" -#include "lltimer.h" -#include "llmath.h" - -#include "llclipboard.h" -#include "llemojihelper.h" -#include "llscrollbar.h" -#include "llstl.h" -#include "llstring.h" -#include "llkeyboard.h" -#include "llkeywords.h" -#include "llundo.h" -#include "llviewborder.h" -#include "llcontrol.h" -#include "llwindow.h" -#include "lltextparser.h" -#include "llscrollcontainer.h" -#include "llspellcheck.h" -#include "llpanel.h" -#include "llurlregistry.h" -#include "lltooltip.h" -#include "llmenugl.h" - -#include -#include "llcombobox.h" - -// -// Globals -// -static LLDefaultChildRegistry::Register r("simple_text_editor"); - -// Compiler optimization, generate extern template -template class LLTextEditor* LLView::getChild( - const std::string& name, bool recurse) const; - -// -// Constants -// -const S32 SPACES_PER_TAB = 4; -const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on - -/////////////////////////////////////////////////////////////////// - -class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd -{ -public: - TextCmdInsert(S32 pos, bool group_with_next, const LLWString &ws, LLTextSegmentPtr segment) - : TextCmd(pos, group_with_next, segment), mWString(ws) - { - } - virtual ~TextCmdInsert() {} - virtual bool execute( LLTextBase* editor, S32* delta ) - { - *delta = insert(editor, getPosition(), mWString ); - LLWStringUtil::truncate(mWString, *delta); - //mWString = wstring_truncate(mWString, *delta); - return (*delta != 0); - } - virtual S32 undo( LLTextBase* editor ) - { - remove(editor, getPosition(), mWString.length() ); - return getPosition(); - } - virtual S32 redo( LLTextBase* editor ) - { - insert(editor, getPosition(), mWString ); - return getPosition() + mWString.length(); - } - -private: - LLWString mWString; -}; - -/////////////////////////////////////////////////////////////////// -class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd -{ -public: - TextCmdAddChar( S32 pos, bool group_with_next, llwchar wc, LLTextSegmentPtr segment) - : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(false) - { - } - virtual void blockExtensions() - { - mBlockExtensions = true; - } - virtual bool canExtend(S32 pos) const - { - // cannot extend text with custom segments - if (!mSegments.empty()) return false; - - return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length()); - } - virtual bool execute( LLTextBase* editor, S32* delta ) - { - *delta = insert(editor, getPosition(), mWString); - LLWStringUtil::truncate(mWString, *delta); - //mWString = wstring_truncate(mWString, *delta); - return (*delta != 0); - } - virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta ) - { - LLWString ws; - ws += wc; - - *delta = insert(editor, pos, ws); - if( *delta > 0 ) - { - mWString += wc; - } - return (*delta != 0); - } - virtual S32 undo( LLTextBase* editor ) - { - remove(editor, getPosition(), mWString.length() ); - return getPosition(); - } - virtual S32 redo( LLTextBase* editor ) - { - insert(editor, getPosition(), mWString ); - return getPosition() + mWString.length(); - } - -private: - LLWString mWString; - bool mBlockExtensions; - -}; - -/////////////////////////////////////////////////////////////////// - -class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd -{ -public: - TextCmdOverwriteChar( S32 pos, bool group_with_next, llwchar wc) - : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} - - virtual bool execute( LLTextBase* editor, S32* delta ) - { - mOldChar = editor->getWText()[getPosition()]; - overwrite(editor, getPosition(), mChar); - *delta = 0; - return true; - } - virtual S32 undo( LLTextBase* editor ) - { - overwrite(editor, getPosition(), mOldChar); - return getPosition(); - } - virtual S32 redo( LLTextBase* editor ) - { - overwrite(editor, getPosition(), mChar); - return getPosition()+1; - } - -private: - llwchar mChar; - llwchar mOldChar; -}; - -/////////////////////////////////////////////////////////////////// - -class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd -{ -public: - TextCmdRemove( S32 pos, bool group_with_next, S32 len, segment_vec_t& segments ) : - TextCmd(pos, group_with_next), mLen(len) - { - std::swap(mSegments, segments); - } - virtual bool execute( LLTextBase* editor, S32* delta ) - { - mWString = editor->getWText().substr(getPosition(), mLen); - *delta = remove(editor, getPosition(), mLen ); - return (*delta != 0); - } - virtual S32 undo( LLTextBase* editor ) - { - insert(editor, getPosition(), mWString); - return getPosition() + mWString.length(); - } - virtual S32 redo( LLTextBase* editor ) - { - remove(editor, getPosition(), mLen ); - return getPosition(); - } -private: - LLWString mWString; - S32 mLen; -}; - - -/////////////////////////////////////////////////////////////////// -LLTextEditor::Params::Params() -: default_text("default_text"), - prevalidator("prevalidator"), - embedded_items("embedded_items", false), - ignore_tab("ignore_tab", true), - auto_indent("auto_indent", true), - default_color("default_color"), - commit_on_focus_lost("commit_on_focus_lost", false), - show_context_menu("show_context_menu"), - show_emoji_helper("show_emoji_helper"), - enable_tooltip_paste("enable_tooltip_paste") -{ - addSynonym(prevalidator, "prevalidate_callback"); - addSynonym(prevalidator, "text_type"); -} - -LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : - LLTextBase(p), - mAutoreplaceCallback(), - mBaseDocIsPristine(true), - mPristineCmd( NULL ), - mLastCmd( NULL ), - mDefaultColor( p.default_color() ), - mAutoIndent(p.auto_indent), - mParseOnTheFly(false), - mCommitOnFocusLost( p.commit_on_focus_lost), - mAllowEmbeddedItems( p.embedded_items ), - mMouseDownX(0), - mMouseDownY(0), - mTabsToNextField(p.ignore_tab), - mPrevalidator(p.prevalidator()), - mShowContextMenu(p.show_context_menu), - mShowEmojiHelper(p.show_emoji_helper), - mEnableTooltipPaste(p.enable_tooltip_paste), - mPassDelete(false), - mKeepSelectionOnReturn(false) -{ - mSourceID.generate(); - - //FIXME: use image? - LLViewBorder::Params params; - params.name = "text ed border"; - params.rect = getLocalRect(); - params.bevel_style = LLViewBorder::BEVEL_IN; - params.border_thickness = 1; - params.visible = p.border_visible; - mBorder = LLUICtrlFactory::create (params); - addChild( mBorder ); - setText(p.default_text()); - - mParseOnTheFly = true; -} - -void LLTextEditor::initFromParams( const LLTextEditor::Params& p) -{ - LLTextBase::initFromParams(p); - - // HACK: text editors always need to be enabled so that we can scroll - LLView::setEnabled(true); - - if (p.commit_on_focus_lost.isProvided()) - { - mCommitOnFocusLost = p.commit_on_focus_lost; - } - - updateAllowingLanguageInput(); -} - -LLTextEditor::~LLTextEditor() -{ - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid - - // Scrollbar is deleted by LLView - std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); - mUndoStack.clear(); - // Mark the menu as dead or its retained in memory till shutdown. - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if(menu) - { - menu->die(); - mContextMenuHandle.markDead(); - } -} - -//////////////////////////////////////////////////////////// -// LLTextEditor -// Public methods - -void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params) -{ - // validate incoming text if necessary - if (mPrevalidator) - { - if (!mPrevalidator.validate(utf8str)) - { - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - - // not valid text, nothing to do - return; - } - } - - blockUndo(); - deselect(); - - mParseOnTheFly = false; - LLTextBase::setText(utf8str, input_params); - mParseOnTheFly = true; - - resetDirty(); -} - -void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap) -{ - if (search_text_in.empty()) - { - return; - } - - LLWString text = getWText(); - LLWString search_text = utf8str_to_wstring(search_text_in); - if (case_insensitive) - { - LLWStringUtil::toLower(text); - LLWStringUtil::toLower(search_text); - } - - if (mIsSelecting) - { - LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd); - - if (selected_text == search_text) - { - // We already have this word selected, we are searching for the next. - setCursorPos(mCursorPos + search_text.size()); - } - } - - S32 loc = text.find(search_text,mCursorPos); - - // If Maybe we wrapped, search again - if (wrap && (-1 == loc)) - { - loc = text.find(search_text); - } - - // If still -1, then search_text just isn't found. - if (-1 == loc) - { - mIsSelecting = false; - mSelectionEnd = 0; - mSelectionStart = 0; - return; - } - - setCursorPos(loc); - - mIsSelecting = true; - mSelectionEnd = mCursorPos; - mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size())); -} - -bool LLTextEditor::replaceText(const std::string& search_text_in, const std::string& replace_text, - bool case_insensitive, bool wrap) -{ - bool replaced = false; - - if (search_text_in.empty()) - { - return replaced; - } - - LLWString search_text = utf8str_to_wstring(search_text_in); - if (mIsSelecting) - { - LLWString text = getWText(); - LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd); - - if (case_insensitive) - { - LLWStringUtil::toLower(selected_text); - LLWStringUtil::toLower(search_text); - } - - if (selected_text == search_text) - { - insertText(replace_text); - replaced = true; - } - } - - selectNext(search_text_in, case_insensitive, wrap); - return replaced; -} - -void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive) -{ - startOfDoc(); - selectNext(search_text, case_insensitive, false); - - bool replaced = true; - while ( replaced ) - { - replaced = replaceText(search_text,replace_text, case_insensitive, false); - } -} - -S32 LLTextEditor::prevWordPos(S32 cursorPos) const -{ - LLWString wtext(getWText()); - while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') ) - { - cursorPos--; - } - while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) - { - cursorPos--; - } - return cursorPos; -} - -S32 LLTextEditor::nextWordPos(S32 cursorPos) const -{ - LLWString wtext(getWText()); - while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) - { - cursorPos++; - } - while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') ) - { - cursorPos++; - } - return cursorPos; -} - -const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const -{ - static LLPointer index_segment = new LLIndexSegment; - - index_segment->setStart(mCursorPos); - index_segment->setEnd(mCursorPos); - - // find segment index at character to left of cursor (or rightmost edge of selection) - segment_set_t::const_iterator it = mSegments.lower_bound(index_segment); - - if (it != mSegments.end()) - { - return *it; - } - else - { - return LLTextSegmentPtr(); - } -} - -void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const -{ - S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos; - S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos; - - return getSegmentsInRange(segments, left, right, true); -} - -void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const -{ - segment_set_t::const_iterator first_it = getSegIterContaining(start); - segment_set_t::const_iterator end_it = getSegIterContaining(end - 1); - if (end_it != mSegments.end()) ++end_it; - - for (segment_set_t::const_iterator it = first_it; it != end_it; ++it) - { - LLTextSegmentPtr segment = *it; - if (include_partial - || (segment->getStart() >= start - && segment->getEnd() <= end)) - { - segments_out.push_back(segment); - } - } -} - -void LLTextEditor::setShowEmojiHelper(bool show) -{ - if (!mShowEmojiHelper) - { - LLEmojiHelper::instance().hideHelper(this); - } - - mShowEmojiHelper = show; -} - -bool LLTextEditor::selectionContainsLineBreaks() -{ - if (hasSelection()) - { - S32 left = llmin(mSelectionStart, mSelectionEnd); - S32 right = left + llabs(mSelectionStart - mSelectionEnd); - - LLWString wtext = getWText(); - for( S32 i = left; i < right; i++ ) - { - if (wtext[i] == '\n') - { - return true; - } - } - } - return false; -} - - -S32 LLTextEditor::indentLine( S32 pos, S32 spaces ) -{ - // Assumes that pos is at the start of the line - // spaces may be positive (indent) or negative (unindent). - // Returns the actual number of characters added or removed. - - llassert(pos >= 0); - llassert(pos <= getLength() ); - - S32 delta_spaces = 0; - - if (spaces >= 0) - { - // Indent - for(S32 i=0; i < spaces; i++) - { - delta_spaces += addChar(pos, ' '); - } - } - else - { - // Unindent - for(S32 i=0; i < -spaces; i++) - { - LLWString wtext = getWText(); - if (wtext[pos] == ' ') - { - delta_spaces += remove( pos, 1, false ); - } - } - } - - return delta_spaces; -} - -void LLTextEditor::indentSelectedLines( S32 spaces ) -{ - if( hasSelection() ) - { - LLWString text = getWText(); - S32 left = llmin( mSelectionStart, mSelectionEnd ); - S32 right = left + llabs( mSelectionStart - mSelectionEnd ); - bool cursor_on_right = (mSelectionEnd > mSelectionStart); - S32 cur = left; - - // Expand left to start of line - while( (cur > 0) && (text[cur] != '\n') ) - { - cur--; - } - left = cur; - if( cur > 0 ) - { - left++; - } - - // Expand right to end of line - if( text[right - 1] == '\n' ) - { - right--; - } - else - { - while( (text[right] != '\n') && (right <= getLength() ) ) - { - right++; - } - } - - // Disabling parsing on the fly to avoid updating text segments - // until all indentation commands are executed. - mParseOnTheFly = false; - - // Find each start-of-line and indent it - do - { - if( text[cur] == '\n' ) - { - cur++; - } - - S32 delta_spaces = indentLine( cur, spaces ); - if( delta_spaces > 0 ) - { - cur += delta_spaces; - } - right += delta_spaces; - - text = getWText(); - - // Find the next new line - while( (cur < right) && (text[cur] != '\n') ) - { - cur++; - } - } - while( cur < right ); - - mParseOnTheFly = true; - - if( (right < getLength()) && (text[right] == '\n') ) - { - right++; - } - - // Set the selection and cursor - if( cursor_on_right ) - { - mSelectionStart = left; - mSelectionEnd = right; - } - else - { - mSelectionStart = right; - mSelectionEnd = left; - } - setCursorPos(mSelectionEnd); - } -} - -//virtual -bool LLTextEditor::canSelectAll() const -{ - return true; -} - -// virtual -void LLTextEditor::selectAll() -{ - mSelectionStart = getLength(); - mSelectionEnd = 0; - setCursorPos(mSelectionEnd); - updatePrimary(); -} - -void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos) -{ - setCursorPos(prev_cursor_pos); - startSelection(); - setCursorPos(next_cursor_pos); - endSelection(); -} - -void LLTextEditor::insertEmoji(llwchar emoji) -{ - LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL; - auto styleParams = LLStyle::Params(); - styleParams.font = LLFontGL::getFontEmojiLarge(); - auto segment = new LLEmojiTextSegment(new LLStyle(styleParams), mCursorPos, mCursorPos + 1, *this); - insert(mCursorPos, LLWString(1, emoji), false, segment); - setCursorPos(mCursorPos + 1); -} - -void LLTextEditor::handleEmojiCommit(llwchar emoji) -{ - S32 shortCodePos; - if (LLEmojiHelper::isCursorInEmojiCode(getWText(), mCursorPos, &shortCodePos)) - { - remove(shortCodePos, mCursorPos - shortCodePos, true); - setCursorPos(shortCodePos); - - insertEmoji(emoji); - } -} - -bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // set focus first, in case click callbacks want to change it - // RN: do we really need to have a tab stop? - if (hasTabStop()) - { - setFocus( true ); - } - - // Let scrollbar have first dibs - handled = LLTextBase::handleMouseDown(x, y, mask); - - if( !handled ) - { - if (!(mask & MASK_SHIFT)) - { - deselect(); - } - - bool start_select = true; - if( start_select ) - { - // If we're not scrolling (handled by child), then we're selecting - if (mask & MASK_SHIFT) - { - S32 old_cursor_pos = mCursorPos; - setCursorAtLocalPos( x, y, true ); - - if (hasSelection()) - { - mSelectionEnd = mCursorPos; - } - else - { - mSelectionStart = old_cursor_pos; - mSelectionEnd = mCursorPos; - } - // assume we're starting a drag select - mIsSelecting = true; - } - else - { - setCursorAtLocalPos( x, y, true ); - startSelection(); - } - } - - handled = true; - } - - // Delay cursor flashing - resetCursorBlink(); - - if (handled && !gFocusMgr.getMouseCapture()) - { - gFocusMgr.setMouseCapture( this ); - } - return handled; -} - -bool LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - if (hasTabStop()) - { - setFocus(true); - } - - bool show_menu = false; - - // Prefer editor menu if it has selection. See EXT-6806. - if (hasSelection()) - { - S32 click_pos = getDocIndexFromLocalCoord(x, y, false); - if (click_pos > mSelectionStart && click_pos < mSelectionEnd) - { - show_menu = true; - } - } - - // Let segments handle the click, if nothing does, show editor menu - if (!show_menu && !LLTextBase::handleRightMouseDown(x, y, mask)) - { - show_menu = true; - } - - if (show_menu && getShowContextMenu()) - { - showContextMenu(x, y); - } - - return true; -} - - - -bool LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - if (hasTabStop()) - { - setFocus(true); - } - - if (!LLTextBase::handleMouseDown(x, y, mask)) - { - if( canPastePrimary() ) - { - setCursorAtLocalPos( x, y, true ); - // does not rely on focus being set - pastePrimary(); - } - } - return true; -} - - -bool LLTextEditor::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - if(hasMouseCapture() ) - { - if( mIsSelecting ) - { - if(mScroller) - { - mScroller->autoScroll(x, y); - } - S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); - S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); - setCursorAtLocalPos( clamped_x, clamped_y, true ); - mSelectionEnd = mCursorPos; - } - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - getWindow()->setCursor(UI_CURSOR_IBEAM); - handled = true; - } - - if( !handled ) - { - // Pass to children - handled = LLTextBase::handleHover(x, y, mask); - } - - if( handled ) - { - // Delay cursor flashing - resetCursorBlink(); - } - - if( !handled ) - { - getWindow()->setCursor(UI_CURSOR_IBEAM); - handled = true; - } - - return handled; -} - - -bool LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // if I'm not currently selecting text - if (!(mIsSelecting && hasMouseCapture())) - { - // let text segments handle mouse event - handled = LLTextBase::handleMouseUp(x, y, mask); - } - - if( !handled ) - { - if( mIsSelecting ) - { - if(mScroller) - { - mScroller->autoScroll(x, y); - } - S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); - S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); - setCursorAtLocalPos( clamped_x, clamped_y, true ); - endSelection(); - } - - // take selection to 'primary' clipboard - updatePrimary(); - - handled = true; - } - - // Delay cursor flashing - resetCursorBlink(); - - if( hasMouseCapture() ) - { - gFocusMgr.setMouseCapture( NULL ); - - handled = true; - } - - return handled; -} - - -bool LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // let scrollbar and text segments have first dibs - handled = LLTextBase::handleDoubleClick(x, y, mask); - - if( !handled ) - { - setCursorAtLocalPos( x, y, false ); - deselect(); - - LLWString text = getWText(); - - if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) ) - { - // Select word the cursor is over - while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1])) - { - if (!setCursorPos(mCursorPos - 1)) break; - } - startSelection(); - - while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) ) - { - if (!setCursorPos(mCursorPos + 1)) break; - } - - mSelectionEnd = mCursorPos; - } - else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) ) - { - // Select the character the cursor is over - startSelection(); - setCursorPos(mCursorPos + 1); - mSelectionEnd = mCursorPos; - } - - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection here. - mIsSelecting = false; - - // delay cursor flashing - resetCursorBlink(); - - // take selection to 'primary' clipboard - updatePrimary(); - - handled = true; - } - - return handled; -} - - -//---------------------------------------------------------------------------- -// Returns change in number of characters in mText - -S32 LLTextEditor::execute( TextCmd* cmd ) -{ - if (!mReadOnly && mShowEmojiHelper) - { - // Any change to our contents should always hide the helper - LLEmojiHelper::instance().hideHelper(this); - } - - S32 delta = 0; - if( cmd->execute(this, &delta) ) - { - // Delete top of undo stack - undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); - std::for_each(mUndoStack.begin(), enditer, DeletePointer()); - mUndoStack.erase(mUndoStack.begin(), enditer); - // Push the new command is now on the top (front) of the undo stack. - mUndoStack.push_front(cmd); - mLastCmd = cmd; - - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(getViewModel()->getDisplay()); - if (need_to_rollback) - { - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - - // get rid of this last command and clean up undo stack - undo(); - - // remove any evidence of this command from redo history - mUndoStack.pop_front(); - delete cmd; - - // failure, nothing changed - delta = 0; - } - } - else - { - // Operation failed, so don't put it on the undo stack. - delete cmd; - } - - return delta; -} - -S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) -{ - return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) ); -} - -S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) -{ - S32 end_pos = getEditableIndex(pos + length, true); - bool removedChar = false; - - segment_vec_t segments_to_remove; - // store text segments - getSegmentsInRange(segments_to_remove, pos, pos + length, false); - - if (pos <= end_pos) - { - removedChar = execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) ); - } - - return removedChar; -} - -S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) -{ - if ((S32)getLength() == pos) - { - return addChar(pos, wc); - } - else - { - return execute(new TextCmdOverwriteChar(pos, false, wc)); - } -} - -// Remove a single character from the text. Tries to remove -// a pseudo-tab (up to for spaces in a row) -void LLTextEditor::removeCharOrTab() -{ - if (!getEnabled()) - { - return; - } - - if (mCursorPos > 0) - { - S32 chars_to_remove = 1; - - LLWString text = getWText(); - if (text[mCursorPos - 1] == ' ') - { - // Try to remove a "tab" - S32 offset = getLineOffsetFromDocIndex(mCursorPos); - if (offset > 0) - { - chars_to_remove = offset % SPACES_PER_TAB; - if (chars_to_remove == 0) - { - chars_to_remove = SPACES_PER_TAB; - } - - for (S32 i = 0; i < chars_to_remove; i++) - { - if (text[mCursorPos - i - 1] != ' ') - { - // Fewer than a full tab's worth of spaces, so - // just delete a single character. - chars_to_remove = 1; - break; - } - } - } - } - - for (S32 i = 0; i < chars_to_remove; i++) - { - setCursorPos(mCursorPos - 1); - remove(mCursorPos, 1, false); - } - - tryToShowEmojiHelper(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } -} - -// Remove a single character from the text -S32 LLTextEditor::removeChar(S32 pos) -{ - return remove(pos, 1, false); -} - -void LLTextEditor::removeChar() -{ - if (!getEnabled()) - { - return; - } - - if (mCursorPos > 0) - { - setCursorPos(mCursorPos - 1); - removeChar(mCursorPos); - tryToShowEmojiHelper(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } -} - -// Add a single character to the text -S32 LLTextEditor::addChar(S32 pos, llwchar wc) -{ - if ((wstring_utf8_length(getWText()) + wchar_utf8_length(wc)) > mMaxTextByteLength) - { - LLUI::getInstance()->reportBadKeystroke(); - return 0; - } - - if (mLastCmd && mLastCmd->canExtend(pos)) - { - if (mPrevalidator) - { - // get a copy of current text contents - LLWString test_string(getViewModel()->getDisplay()); - - // modify text contents as if this addChar succeeded - llassert(pos <= (S32)test_string.size()); - test_string.insert(pos, 1, wc); - if (!mPrevalidator.validate(test_string)) - { - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - return 0; - } - } - - S32 delta = 0; - mLastCmd->extendAndExecute(this, pos, wc, &delta); - - return delta; - } - - return execute(new TextCmdAddChar(pos, false, wc, LLTextSegmentPtr())); -} - -void LLTextEditor::addChar(llwchar wc) -{ - if (!getEnabled()) - { - return; - } - - if (hasSelection()) - { - deleteSelection(true); - } - else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - removeChar(mCursorPos); - } - - setCursorPos(mCursorPos + addChar( mCursorPos, wc )); - tryToShowEmojiHelper(); - - if (!mReadOnly && mAutoreplaceCallback != NULL) - { - // autoreplace the text, if necessary - S32 replacement_start; - S32 replacement_length; - LLWString replacement_string; - S32 new_cursor_pos = mCursorPos; - mAutoreplaceCallback(replacement_start, replacement_length, replacement_string, new_cursor_pos, getWText()); - - if (replacement_length > 0 || !replacement_string.empty()) - { - remove(replacement_start, replacement_length, true); - insert(replacement_start, replacement_string, false, LLTextSegmentPtr()); - setCursorPos(new_cursor_pos); - } - } -} - -void LLTextEditor::showEmojiHelper() -{ - if (mReadOnly || !mShowEmojiHelper) - return; - - const LLRect cursorRect(getLocalRectFromDocIndex(mCursorPos)); - auto cb = [this](llwchar emoji) { insertEmoji(emoji); }; - LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb); -} - -void LLTextEditor::tryToShowEmojiHelper() -{ - if (mReadOnly || !mShowEmojiHelper) - return; - - S32 shortCodePos; - LLWString wtext(getWText()); - if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos)) - { - const LLRect cursorRect(getLocalRectFromDocIndex(shortCodePos)); - const LLWString wpart(wtext.substr(shortCodePos, mCursorPos - shortCodePos)); - const std::string part(wstring_to_utf8str(wpart)); - auto cb = [this](llwchar emoji) { handleEmojiCommit(emoji); }; - LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, part, cb); - } - else - { - LLEmojiHelper::instance().hideHelper(); - } -} - -void LLTextEditor::addLineBreakChar(bool group_together) -{ - if( !getEnabled() ) - { - return; - } - if( hasSelection() ) - { - deleteSelection(true); - } - else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - removeChar(mCursorPos); - } - - LLStyleConstSP sp(new LLStyle(LLStyle::Params())); - LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos); - - S32 pos = execute(new TextCmdAddChar(mCursorPos, group_together, '\n', segment)); - - setCursorPos(mCursorPos + pos); -} - - -bool LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) -{ - bool handled = false; - - if( mask & MASK_SHIFT ) - { - handled = true; - - switch( key ) - { - case KEY_LEFT: - if( 0 < mCursorPos ) - { - startSelection(); - setCursorPos(mCursorPos - 1); - if( mask & MASK_CONTROL ) - { - setCursorPos(prevWordPos(mCursorPos)); - } - mSelectionEnd = mCursorPos; - } - break; - - case KEY_RIGHT: - if( mCursorPos < getLength() ) - { - startSelection(); - setCursorPos(mCursorPos + 1); - if( mask & MASK_CONTROL ) - { - setCursorPos(nextWordPos(mCursorPos)); - } - mSelectionEnd = mCursorPos; - } - break; - - case KEY_UP: - startSelection(); - changeLine( -1 ); - mSelectionEnd = mCursorPos; - break; - - case KEY_PAGE_UP: - startSelection(); - changePage( -1 ); - mSelectionEnd = mCursorPos; - break; - - case KEY_HOME: - startSelection(); - if( mask & MASK_CONTROL ) - { - setCursorPos(0); - } - else - { - startOfLine(); - } - mSelectionEnd = mCursorPos; - break; - - case KEY_DOWN: - startSelection(); - changeLine( 1 ); - mSelectionEnd = mCursorPos; - break; - - case KEY_PAGE_DOWN: - startSelection(); - changePage( 1 ); - mSelectionEnd = mCursorPos; - break; - - case KEY_END: - startSelection(); - if( mask & MASK_CONTROL ) - { - setCursorPos(getLength()); - } - else - { - endOfLine(); - } - mSelectionEnd = mCursorPos; - break; - - default: - handled = false; - break; - } - } - - if( handled ) - { - // take selection to 'primary' clipboard - updatePrimary(); - } - - return handled; -} - -bool LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) -{ - bool handled = false; - - // Ignore capslock key - if( MASK_NONE == mask ) - { - handled = true; - switch( key ) - { - case KEY_UP: - changeLine( -1 ); - break; - - case KEY_PAGE_UP: - changePage( -1 ); - break; - - case KEY_HOME: - startOfLine(); - break; - - case KEY_DOWN: - changeLine( 1 ); - deselect(); - break; - - case KEY_PAGE_DOWN: - changePage( 1 ); - break; - - case KEY_END: - endOfLine(); - break; - - case KEY_LEFT: - if( hasSelection() ) - { - setCursorPos(llmin( mSelectionStart, mSelectionEnd )); - } - else - { - if( 0 < mCursorPos ) - { - setCursorPos(mCursorPos - 1); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - } - break; - - case KEY_RIGHT: - if( hasSelection() ) - { - setCursorPos(llmax( mSelectionStart, mSelectionEnd )); - } - else - { - if( mCursorPos < getLength() ) - { - setCursorPos(mCursorPos + 1); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - } - break; - - default: - handled = false; - break; - } - } - - if (handled) - { - deselect(); - } - - return handled; -} - -void LLTextEditor::deleteSelection(bool group_with_next_op ) -{ - if( getEnabled() && hasSelection() ) - { - S32 pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - - remove( pos, length, group_with_next_op ); - - deselect(); - setCursorPos(pos); - } -} - -// virtual -bool LLTextEditor::canCut() const -{ - return !mReadOnly && hasSelection(); -} - -// cut selection to clipboard -void LLTextEditor::cut() -{ - if( !canCut() ) - { - return; - } - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - LLClipboard::instance().copyToClipboard( getWText(), left_pos, length); - deleteSelection( false ); - - onKeyStroke(); -} - -bool LLTextEditor::canCopy() const -{ - return hasSelection(); -} - -// copy selection to clipboard -void LLTextEditor::copy() -{ - if( !canCopy() ) - { - return; - } - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - LLClipboard::instance().copyToClipboard(getWText(), left_pos, length); -} - -bool LLTextEditor::canPaste() const -{ - return !mReadOnly && LLClipboard::instance().isTextAvailable(); -} - -// paste from clipboard -void LLTextEditor::paste() -{ - bool is_primary = false; - pasteHelper(is_primary); -} - -// paste from primary -void LLTextEditor::pastePrimary() -{ - bool is_primary = true; - pasteHelper(is_primary); -} - -// paste from primary (itsprimary==true) or clipboard (itsprimary==false) -void LLTextEditor::pasteHelper(bool is_primary) -{ - struct BoolReset - { - BoolReset(bool& value) : mValuePtr(&value) { *mValuePtr = false; } - ~BoolReset() { *mValuePtr = true; } - bool* mValuePtr; - } reset(mParseOnTheFly); - - bool can_paste_it; - if (is_primary) - { - can_paste_it = canPastePrimary(); - } - else - { - can_paste_it = canPaste(); - } - - if (!can_paste_it) - { - return; - } - - LLWString paste; - LLClipboard::instance().pasteFromClipboard(paste, is_primary); - - if (paste.empty()) - { - return; - } - - // Delete any selected characters (the paste replaces them) - if( (!is_primary) && hasSelection() ) - { - deleteSelection(true); - } - - // Clean up string (replace tabs and remove characters that our fonts don't support). - LLWString clean_string(paste); - cleanStringForPaste(clean_string); - - // Insert the new text into the existing text. - - //paste text with linebreaks. - pasteTextWithLinebreaks(clean_string); - - deselect(); - - onKeyStroke(); -} - - -// Clean up string (replace tabs and remove characters that our fonts don't support). -void LLTextEditor::cleanStringForPaste(LLWString & clean_string) -{ - std::string clean_string_utf = wstring_to_utf8str(clean_string); - std::replace( clean_string_utf.begin(), clean_string_utf.end(), '\r', '\n'); - clean_string = utf8str_to_wstring(clean_string_utf); - - LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB); - if( mAllowEmbeddedItems ) - { - const llwchar LF = 10; - S32 len = clean_string.length(); - for( S32 i = 0; i < len; i++ ) - { - llwchar wc = clean_string[i]; - if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) ) - { - clean_string[i] = LL_UNKNOWN_CHAR; - } - else if (wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR) - { - clean_string[i] = pasteEmbeddedItem(wc); - } - } - } -} - - -void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string) -{ - std::basic_string::size_type start = 0; - std::basic_string::size_type pos = clean_string.find('\n',start); - - while((pos != -1) && (pos != clean_string.length() -1)) - { - if(pos!=start) - { - std::basic_string str = std::basic_string(clean_string,start,pos-start); - setCursorPos(mCursorPos + insert(mCursorPos, str, true, LLTextSegmentPtr())); - } - addLineBreakChar(true); // Add a line break and group with the next addition. - - start = pos+1; - pos = clean_string.find('\n',start); - } - - if (pos != start) - { - std::basic_string str = std::basic_string(clean_string,start,clean_string.length()-start); - setCursorPos(mCursorPos + insert(mCursorPos, str, false, LLTextSegmentPtr())); - } - else - { - addLineBreakChar(false); // Add a line break and end the grouping. - } -} - -// copy selection to primary -void LLTextEditor::copyPrimary() -{ - if( !canCopy() ) - { - return; - } - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true); -} - -bool LLTextEditor::canPastePrimary() const -{ - return !mReadOnly && LLClipboard::instance().isTextAvailable(true); -} - -void LLTextEditor::updatePrimary() -{ - if (canCopy()) - { - copyPrimary(); - } -} - -bool LLTextEditor::handleControlKey(const KEY key, const MASK mask) -{ - bool handled = false; - - if( mask & MASK_CONTROL ) - { - handled = true; - - switch( key ) - { - case KEY_HOME: - if( mask & MASK_SHIFT ) - { - startSelection(); - setCursorPos(0); - mSelectionEnd = mCursorPos; - } - else - { - // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down - // all move the cursor as if clicking, so should deselect. - deselect(); - startOfDoc(); - } - break; - - case KEY_END: - { - if( mask & MASK_SHIFT ) - { - startSelection(); - } - else - { - // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down - // all move the cursor as if clicking, so should deselect. - deselect(); - } - endOfDoc(); - if( mask & MASK_SHIFT ) - { - mSelectionEnd = mCursorPos; - } - break; - } - - case KEY_RIGHT: - if( mCursorPos < getLength() ) - { - // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down - // all move the cursor as if clicking, so should deselect. - deselect(); - - setCursorPos(nextWordPos(mCursorPos + 1)); - } - break; - - - case KEY_LEFT: - if( mCursorPos > 0 ) - { - // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down - // all move the cursor as if clicking, so should deselect. - deselect(); - - setCursorPos(prevWordPos(mCursorPos - 1)); - } - break; - - default: - handled = false; - break; - } - } - - if (handled && !gFocusMgr.getMouseCapture()) - { - updatePrimary(); - } - - return handled; -} - - -bool LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) - { - bool handled = true; - - if (mReadOnly) return false; - - switch( key ) - { - case KEY_INSERT: - if (mask == MASK_NONE) - { - gKeyboard->toggleInsertMode(); - } - break; - - case KEY_BACKSPACE: - if( hasSelection() ) - { - deleteSelection(false); - } - else - if( 0 < mCursorPos ) - { - removeCharOrTab(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - break; - - - case KEY_RETURN: - if (mask == MASK_NONE) - { - if( hasSelection() && !mKeepSelectionOnReturn ) - { - deleteSelection(false); - } - if (mAutoIndent) - { - autoIndent(); - } - } - else - { - handled = false; - break; - } - break; - - case KEY_TAB: - if (mask & MASK_CONTROL) - { - handled = false; - break; - } - if( hasSelection() && selectionContainsLineBreaks() ) - { - indentSelectedLines( (mask & MASK_SHIFT) ? -SPACES_PER_TAB : SPACES_PER_TAB ); - } - else - { - if( hasSelection() ) - { - deleteSelection(false); - } - - S32 offset = getLineOffsetFromDocIndex(mCursorPos); - - S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB); - for( S32 i=0; i < spaces_needed; i++ ) - { - addChar( ' ' ); - } - } - break; - - default: - handled = false; - break; - } - - if (handled) - { - onKeyStroke(); - } - return handled; -} - - -void LLTextEditor::unindentLineBeforeCloseBrace() -{ - if( mCursorPos >= 1 ) - { - LLWString text = getWText(); - if( ' ' == text[ mCursorPos - 1 ] ) - { - S32 line = getLineNumFromDocIndex(mCursorPos, false); - S32 line_start = getLineStart(line); - - // Jump over spaces in the current line - while ((' ' == text[line_start]) && (line_start < mCursorPos)) - { - line_start++; - } - - // Make sure there is nothing but ' ' before the Brace we are unindenting - if (line_start == mCursorPos) - { - removeCharOrTab(); - } - } - } -} - - -bool LLTextEditor::handleKeyHere(KEY key, MASK mask ) -{ - bool handled = false; - - // Special case for TAB. If want to move to next field, report - // not handled and let the parent take care of field movement. - if (KEY_TAB == key && mTabsToNextField) - { - return false; - } - - if (mReadOnly && mScroller) - { - handled = (mScroller && mScroller->handleKeyHere( key, mask )) - || handleSelectionKey(key, mask) - || handleControlKey(key, mask); - } - else - { - if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask)) - { - return true; - } - - if (mEnableTooltipPaste && - LLToolTipMgr::instance().toolTipVisible() && - LLToolTipMgr::instance().isTooltipPastable() && - KEY_TAB == key) - { // Paste the first line of a tooltip into the editor - std::string message; - LLToolTipMgr::instance().getToolTipMessage(message); - LLWString tool_tip_text(utf8str_to_wstring(message)); - - if (tool_tip_text.size() > 0) - { - // Delete any selected characters (the tooltip text replaces them) - if(hasSelection()) - { - deleteSelection(true); - } - - std::basic_string::size_type pos = tool_tip_text.find('\n',0); - if (pos != -1) - { // Extract the first line of the tooltip - tool_tip_text = std::basic_string(tool_tip_text, 0, pos); - } - - // Add the text - cleanStringForPaste(tool_tip_text); - pasteTextWithLinebreaks(tool_tip_text); - handled = true; - } - } - else - { // Normal key handling - handled = handleNavigationKey( key, mask ) - || handleSelectionKey(key, mask) - || handleControlKey(key, mask) - || handleSpecialKey(key, mask); - } - } - - if( handled ) - { - resetCursorBlink(); - needsScroll(); - - if (mShowEmojiHelper) - { - // Dismiss the helper whenever we handled a key that it didn't - LLEmojiHelper::instance().hideHelper(this); - } - } - - return handled; -} - - -bool LLTextEditor::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return false; - } - - bool handled = false; - - // Handle most keys only if the text editor is writeable. - if( !mReadOnly ) - { - if (mShowEmojiHelper && uni_char < 0x80 && LLEmojiHelper::instance().handleKey(this, (KEY)uni_char, MASK_NONE)) - { - return true; - } - - if( mAutoIndent && '}' == uni_char ) - { - unindentLineBeforeCloseBrace(); - } - - // TODO: KLW Add auto show of tool tip on ( - addChar( uni_char ); - - // Keys that add characters temporarily hide the cursor - getWindow()->hideCursorUntilMouseMove(); - - handled = true; - } - - if( handled ) - { - resetCursorBlink(); - - // Most keystrokes will make the selection box go away, but not all will. - deselect(); - - onKeyStroke(); - } - - return handled; -} - - -// virtual -bool LLTextEditor::canDoDelete() const -{ - return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) ); -} - -void LLTextEditor::doDelete() -{ - if( !canDoDelete() ) - { - return; - } - if( hasSelection() ) - { - deleteSelection(false); - } - else - if( mCursorPos < getLength() ) - { - S32 i; - S32 chars_to_remove = 1; - LLWString text = getWText(); - if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) ) - { - // Try to remove a full tab's worth of spaces - S32 offset = getLineOffsetFromDocIndex(mCursorPos); - chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB); - if( chars_to_remove == 0 ) - { - chars_to_remove = SPACES_PER_TAB; - } - - for( i = 0; i < chars_to_remove; i++ ) - { - if( text[mCursorPos + i] != ' ' ) - { - chars_to_remove = 1; - break; - } - } - } - - for( i = 0; i < chars_to_remove; i++ ) - { - setCursorPos(mCursorPos + 1); - removeChar(); - } - - } - - onKeyStroke(); -} - -//---------------------------------------------------------------------------- - - -void LLTextEditor::blockUndo() -{ - mBaseDocIsPristine = false; - mLastCmd = NULL; - std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); - mUndoStack.clear(); -} - -// virtual -bool LLTextEditor::canUndo() const -{ - return !mReadOnly && mLastCmd != NULL; -} - -void LLTextEditor::undo() -{ - if( !canUndo() ) - { - return; - } - deselect(); - S32 pos = 0; - do - { - pos = mLastCmd->undo(this); - undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); - if (iter != mUndoStack.end()) - ++iter; - if (iter != mUndoStack.end()) - mLastCmd = *iter; - else - mLastCmd = NULL; - - } while( mLastCmd && mLastCmd->groupWithNext() ); - - setCursorPos(pos); - - onKeyStroke(); -} - -bool LLTextEditor::canRedo() const -{ - return !mReadOnly && (mUndoStack.size() > 0) && (mLastCmd != mUndoStack.front()); -} - -void LLTextEditor::redo() -{ - if( !canRedo() ) - { - return; - } - deselect(); - S32 pos = 0; - do - { - if( !mLastCmd ) - { - mLastCmd = mUndoStack.back(); - } - else - { - undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); - if (iter != mUndoStack.begin()) - mLastCmd = *(--iter); - else - mLastCmd = NULL; - } - - if( mLastCmd ) - { - pos = mLastCmd->redo(this); - } - } while( - mLastCmd && - mLastCmd->groupWithNext() && - (mLastCmd != mUndoStack.front()) ); - - setCursorPos(pos); - - onKeyStroke(); -} - -void LLTextEditor::onFocusReceived() -{ - LLTextBase::onFocusReceived(); - updateAllowingLanguageInput(); -} - -void LLTextEditor::focusLostHelper() -{ - updateAllowingLanguageInput(); - - // Route menu back to the default - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - if (mCommitOnFocusLost) - { - onCommit(); - } - - // Make sure cursor is shown again - getWindow()->showCursorFromMouseMove(); -} - -void LLTextEditor::onFocusLost() -{ - focusLostHelper(); - LLTextBase::onFocusLost(); -} - -void LLTextEditor::onCommit() -{ - setControlValue(getValue()); - LLTextBase::onCommit(); -} - -void LLTextEditor::setEnabled(bool enabled) -{ - // just treat enabled as read-only flag - bool read_only = !enabled; - if (read_only != mReadOnly) - { - //mReadOnly = read_only; - LLTextBase::setReadOnly(read_only); - updateSegments(); - updateAllowingLanguageInput(); - } -} - -void LLTextEditor::showContextMenu(S32 x, S32 y) -{ - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if (!menu) - { - llassert(LLMenuGL::sMenuContainer != NULL); - menu = LLUICtrlFactory::createFromFile("menu_text_editor.xml", - LLMenuGL::sMenuContainer, - LLMenuHolderGL::child_registry_t::instance()); - if(!menu) - { - LL_WARNS() << "Failed to create menu for LLTextEditor: " << getName() << LL_ENDL; - return; - } - mContextMenuHandle = menu->getHandle(); - } - - // Route menu to this class - // previously this was done in ::handleRightMoseDown: - //if(hasTabStop()) - // setFocus(true) - why? weird... - // and then inside setFocus - // .... - // gEditMenuHandler = this; - // .... - // but this didn't work in all cases and just weird... - //why not here? - // (all this was done for EXT-4443) - - gEditMenuHandler = this; - - S32 screen_x, screen_y; - localPointToScreen(x, y, &screen_x, &screen_y); - - setCursorAtLocalPos(x, y, false); - if (hasSelection()) - { - if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) ) - { - deselect(); - } - else - { - setCursorPos(llmax(mSelectionStart, mSelectionEnd)); - } - } - - bool use_spellcheck = getSpellCheck(), is_misspelled = false; - if (use_spellcheck) - { - mSuggestionList.clear(); - - // If the cursor is on a misspelled word, retrieve suggestions for it - std::string misspelled_word = getMisspelledWord(mCursorPos); - if ((is_misspelled = !misspelled_word.empty())) - { - LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); - } - } - - menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty())); - menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled)); - menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled)); - menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled)); - menu->show(screen_x, screen_y, this); -} - - -void LLTextEditor::drawPreeditMarker() -{ - static LLUICachedControl preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); - static LLUICachedControl preedit_marker_gap ("UIPreeditMarkerGap", 0); - static LLUICachedControl preedit_marker_position ("UIPreeditMarkerPosition", 0); - static LLUICachedControl preedit_marker_thickness ("UIPreeditMarkerThickness", 0); - static LLUICachedControl preedit_standout_brightness ("UIPreeditStandoutBrightness", 0); - static LLUICachedControl preedit_standout_gap ("UIPreeditStandoutGap", 0); - static LLUICachedControl preedit_standout_position ("UIPreeditStandoutPosition", 0); - static LLUICachedControl preedit_standout_thickness ("UIPreeditStandoutThickness", 0); - - if (!hasPreeditString()) - { - return; - } - - const LLWString textString(getWText()); - const llwchar *text = textString.c_str(); - const S32 text_len = getLength(); - const S32 num_lines = getLineCount(); - - S32 cur_line = getFirstVisibleLine(); - if (cur_line >= num_lines) - { - return; - } - - const S32 line_height = mFont->getLineHeight(); - - S32 line_start = getLineStart(cur_line); - S32 line_y = mVisibleTextRect.mTop - line_height; - while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line)) - { - S32 next_start = -1; - S32 line_end = text_len; - - if ((cur_line + 1) < num_lines) - { - next_start = getLineStart(cur_line + 1); - line_end = next_start; - } - if ( text[line_end-1] == '\n' ) - { - --line_end; - } - - // Does this line contain preedits? - if (line_start >= mPreeditPositions.back()) - { - // We have passed the preedits. - break; - } - if (line_end > mPreeditPositions.front()) - { - for (U32 i = 0; i < mPreeditStandouts.size(); i++) - { - S32 left = mPreeditPositions[i]; - S32 right = mPreeditPositions[i + 1]; - if (right <= line_start || left >= line_end) - { - continue; - } - - line_info& line = mLineInfoList[cur_line]; - LLRect text_rect(line.mRect); - text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents - text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position - - S32 preedit_left = text_rect.mLeft; - if (left > line_start) - { - preedit_left += mFont->getWidth(text, line_start, left - line_start); - } - S32 preedit_right = text_rect.mLeft; - if (right < line_end) - { - preedit_right += mFont->getWidth(text, line_start, right - line_start); - } - else - { - preedit_right += mFont->getWidth(text, line_start, line_end - line_start); - } - - if (mPreeditStandouts[i]) - { - gl_rect_2d(preedit_left + preedit_standout_gap, - text_rect.mBottom + mFont->getDescenderHeight() - 1, - preedit_right - preedit_standout_gap - 1, - text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_standout_thickness, - (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f)); - } - else - { - gl_rect_2d(preedit_left + preedit_marker_gap, - text_rect.mBottom + mFont->getDescenderHeight() - 1, - preedit_right - preedit_marker_gap - 1, - text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_marker_thickness, - (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f)); - } - } - } - - // move down one line - line_y -= line_height; - line_start = next_start; - cur_line++; - } -} - -void LLTextEditor::draw() -{ - { - // pad clipping rectangle so that cursor can draw at full width - // when at left edge of mVisibleTextRect - LLRect clip_rect(mVisibleTextRect); - clip_rect.stretch(1); - LLLocalClipRect clip(clip_rect); - } - - LLTextBase::draw(); - - drawPreeditMarker(); - - //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret - // when in readonly mode - mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly); -} - -// Start or stop the editor from accepting text-editing keystrokes -// see also LLLineEditor -void LLTextEditor::setFocus( bool new_state ) -{ - bool old_state = hasFocus(); - - // Don't change anything if the focus state didn't change - if (new_state == old_state) return; - - // Notify early if we are losing focus. - if (!new_state) - { - getWindow()->allowLanguageTextInput(this, false); - } - - LLTextBase::setFocus( new_state ); - - if( new_state ) - { - // Route menu to this class - gEditMenuHandler = this; - - // Don't start the cursor flashing right away - resetCursorBlink(); - } - else - { - // Route menu back to the default - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - endSelection(); - } -} - -// public -void LLTextEditor::setCursorAndScrollToEnd() -{ - deselect(); - endOfDoc(); -} - -void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap ) -{ - *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap); - *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap); -} - -void LLTextEditor::autoIndent() -{ - // Count the number of spaces in the current line - S32 line = getLineNumFromDocIndex(mCursorPos, false); - S32 line_start = getLineStart(line); - S32 space_count = 0; - S32 i; - - LLWString text = getWText(); - S32 offset = getLineOffsetFromDocIndex(mCursorPos); - while(( ' ' == text[line_start] ) && (space_count < offset)) - { - space_count++; - line_start++; - } - - // If we're starting a braced section, indent one level. - if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') ) - { - space_count += SPACES_PER_TAB; - } - - // Insert that number of spaces on the new line - - //appendLineBreakSegment(LLStyle::Params());//addChar( '\n' ); - addLineBreakChar(); - - for( i = 0; i < space_count; i++ ) - { - addChar( ' ' ); - } -} - -// Inserts new text at the cursor position -void LLTextEditor::insertText(const std::string &new_text) -{ - bool enabled = getEnabled(); - setEnabled( true ); - - // Delete any selected characters (the insertion replaces them) - if( hasSelection() ) - { - deleteSelection(true); - } - - setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), false, LLTextSegmentPtr() )); - - setEnabled( enabled ); -} - -void LLTextEditor::insertText(LLWString &new_text) -{ - bool enabled = getEnabled(); - setEnabled( true ); - - // Delete any selected characters (the insertion replaces them) - if( hasSelection() ) - { - deleteSelection(true); - } - - setCursorPos(mCursorPos + insert( mCursorPos, new_text, false, LLTextSegmentPtr() )); - - setEnabled( enabled ); -} - -void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo) -{ - // Save old state - S32 selection_start = mSelectionStart; - S32 selection_end = mSelectionEnd; - bool was_selecting = mIsSelecting; - S32 cursor_pos = mCursorPos; - S32 old_length = getLength(); - bool cursor_was_at_end = (mCursorPos == old_length); - - deselect(); - - setCursorPos(old_length); - - LLWString widget_wide_text = utf8str_to_wstring(text); - - LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size()); - insert(getLength(), widget_wide_text, false, segment); - - // Set the cursor and scroll position - if( selection_start != selection_end ) - { - mSelectionStart = selection_start; - mSelectionEnd = selection_end; - - mIsSelecting = was_selecting; - setCursorPos(cursor_pos); - } - else if( cursor_was_at_end ) - { - setCursorPos(getLength()); - } - else - { - setCursorPos(cursor_pos); - } - - if (!allow_undo) - { - blockUndo(); - } -} - -void LLTextEditor::removeTextFromEnd(S32 num_chars) -{ - if (num_chars <= 0) return; - - remove(getLength() - num_chars, num_chars, false); - - S32 len = getLength(); - setCursorPos (llclamp(mCursorPos, 0, len)); - mSelectionStart = llclamp(mSelectionStart, 0, len); - mSelectionEnd = llclamp(mSelectionEnd, 0, len); - - needsScroll(); -} - -//---------------------------------------------------------------------------- - -void LLTextEditor::onSpellCheckPerformed() -{ - if (isPristine()) - { - mBaseDocIsPristine = false; - } -} - -void LLTextEditor::makePristine() -{ - mPristineCmd = mLastCmd; - mBaseDocIsPristine = !mLastCmd; - - // Create a clean partition in the undo stack. We don't want a single command to extend from - // the "pre-pristine" state to the "post-pristine" state. - if( mLastCmd ) - { - mLastCmd->blockExtensions(); - } -} - -bool LLTextEditor::isPristine() const -{ - if( mPristineCmd ) - { - return (mPristineCmd == mLastCmd); - } - else - { - // No undo stack, so check if the version before and commands were done was the original version - return !mLastCmd && mBaseDocIsPristine; - } -} - -bool LLTextEditor::tryToRevertToPristineState() -{ - if( !isPristine() ) - { - deselect(); - S32 i = 0; - while( !isPristine() && canUndo() ) - { - undo(); - i--; - } - - while( !isPristine() && canRedo() ) - { - redo(); - i++; - } - - if( !isPristine() ) - { - // failed, so go back to where we started - while( i > 0 ) - { - undo(); - i--; - } - } - } - - return isPristine(); // true => success -} - -void LLTextEditor::updateLinkSegments() -{ - LLWString wtext = getWText(); - - // update any segments that contain a link - for (segment_set_t::iterator it = mSegments.begin(); it != mSegments.end(); ++it) - { - LLTextSegment *segment = *it; - if (segment && segment->getStyle() && segment->getStyle()->isLink()) - { - LLStyleConstSP style = segment->getStyle(); - LLStyleSP new_style(new LLStyle(*style)); - LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart()); - - segment_set_t::const_iterator next_it = mSegments.upper_bound(segment); - LLTextSegment *next_segment = *next_it; - if (next_segment) - { - LLWString next_url_label = wtext.substr(next_segment->getStart(), next_segment->getEnd()-next_segment->getStart()); - std::string link_check = wstring_to_utf8str(url_label) + wstring_to_utf8str(next_url_label); - LLUrlMatch match; - - if ( LLUrlRegistry::instance().findUrl(link_check, match)) - { - if(match.getQuery() == wstring_to_utf8str(next_url_label)) - { - continue; - } - } - } - - // if the link's label (what the user can edit) is a valid Url, - // then update the link's HREF to be the same as the label text. - // This lets users edit Urls in-place. - if (acceptsTextInput() && LLUrlRegistry::instance().hasUrl(url_label)) - { - std::string new_url = wstring_to_utf8str(url_label); - LLStringUtil::trim(new_url); - new_style->setLinkHREF(new_url); - LLStyleConstSP sp(new_style); - segment->setStyle(sp); - } - } - } -} - - - -void LLTextEditor::onMouseCaptureLost() -{ - endSelection(); -} - -/////////////////////////////////////////////////////////////////// -// Hack for Notecards - -bool LLTextEditor::importBuffer(const char* buffer, S32 length ) -{ - std::istringstream instream(buffer); - - // Version 1 format: - // Linden text version 1\n - // {\n - // - // Text length \n - // (text may contain ext_char_values) - // }\n - - char tbuf[MAX_STRING]; /* Flawfinder: ignore */ - - S32 version = 0; - instream.getline(tbuf, MAX_STRING); - if( 1 != sscanf(tbuf, "Linden text version %d", &version) ) - { - LL_WARNS() << "Invalid Linden text file header " << LL_ENDL; - return false; - } - - if( 1 != version ) - { - LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL; - return false; - } - - instream.getline(tbuf, MAX_STRING); - if( 0 != sscanf(tbuf, "{") ) - { - LL_WARNS() << "Invalid Linden text file format" << LL_ENDL; - return false; - } - - S32 text_len = 0; - instream.getline(tbuf, MAX_STRING); - if( 1 != sscanf(tbuf, "Text length %d", &text_len) ) - { - LL_WARNS() << "Invalid Linden text length field" << LL_ENDL; - return false; - } - - if( text_len > mMaxTextByteLength ) - { - LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL; - return false; - } - - bool success = true; - - char* text = new char[ text_len + 1]; - if (text == NULL) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Memory allocation failure." << LL_ENDL; - return false; - } - instream.get(text, text_len + 1, '\0'); - text[text_len] = '\0'; - if( text_len != (S32)strlen(text) )/* Flawfinder: ignore */ - { - LL_WARNS() << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << LL_ENDL;/* Flawfinder: ignore */ - success = false; - } - - instream.getline(tbuf, MAX_STRING); - if( success && (0 != sscanf(tbuf, "}")) ) - { - LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL; - success = false; - } - - if( success ) - { - // Actually set the text - setText( LLStringExplicit(text) ); - } - - delete[] text; - - startOfDoc(); - deselect(); - - return success; -} - -bool LLTextEditor::exportBuffer(std::string &buffer ) -{ - std::ostringstream outstream(buffer); - - outstream << "Linden text version 1\n"; - outstream << "{\n"; - - outstream << llformat("Text length %d\n", getLength() ); - outstream << getText(); - outstream << "}\n"; - - return true; -} - -void LLTextEditor::updateAllowingLanguageInput() -{ - LLWindow* window = getWindow(); - if (!window) - { - // test app, no window available - return; - } - if (hasFocus() && !mReadOnly) - { - window->allowLanguageTextInput(this, true); - } - else - { - window->allowLanguageTextInput(this, false); - } -} - -// Preedit is managed off the undo/redo command stack. - -bool LLTextEditor::hasPreeditString() const -{ - return (mPreeditPositions.size() > 1); -} - -void LLTextEditor::resetPreedit() -{ - if (hasSelection()) - { - if (hasPreeditString()) - { - LL_WARNS() << "Preedit and selection!" << LL_ENDL; - deselect(); - } - else - { - deleteSelection(true); - } - } - if (hasPreeditString()) - { - if (hasSelection()) - { - LL_WARNS() << "Preedit and selection!" << LL_ENDL; - deselect(); - } - - setCursorPos(mPreeditPositions.front()); - removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos); - insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString); - - mPreeditWString.clear(); - mPreeditOverwrittenWString.clear(); - mPreeditPositions.clear(); - - // A call to updatePreedit should soon follow under a - // normal course of operation, so we don't need to - // maintain internal variables such as line start - // positions now. - } -} - -void LLTextEditor::updatePreedit(const LLWString &preedit_string, - const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) -{ - // Just in case. - if (mReadOnly) - { - return; - } - - getWindow()->hideCursorUntilMouseMove(); - - S32 insert_preedit_at = mCursorPos; - - mPreeditWString = preedit_string; - mPreeditPositions.resize(preedit_segment_lengths.size() + 1); - S32 position = insert_preedit_at; - for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) - { - mPreeditPositions[i] = position; - position += preedit_segment_lengths[i]; - } - mPreeditPositions.back() = position; - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length()); - removeStringNoUndo(insert_preedit_at, mPreeditWString.length()); - } - else - { - mPreeditOverwrittenWString.clear(); - } - - segment_vec_t segments; - //pass empty segments to let "insertStringNoUndo" make new LLNormalTextSegment and insert it, if needed. - insertStringNoUndo(insert_preedit_at, mPreeditWString, &segments); - - mPreeditStandouts = preedit_standouts; - - setCursorPos(insert_preedit_at + caret_position); - - // Update of the preedit should be caused by some key strokes. - resetCursorBlink(); - - onKeyStroke(); -} - -bool LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const -{ - if (control) - { - LLRect control_rect_screen; - localRectToScreen(mVisibleTextRect, &control_rect_screen); - LLUI::getInstance()->screenRectToGL(control_rect_screen, control); - } - - S32 preedit_left_position, preedit_right_position; - if (hasPreeditString()) - { - preedit_left_position = mPreeditPositions.front(); - preedit_right_position = mPreeditPositions.back(); - } - else - { - preedit_left_position = preedit_right_position = mCursorPos; - } - - const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos); - if (query < preedit_left_position || query > preedit_right_position) - { - return false; - } - - const S32 first_visible_line = getFirstVisibleLine(); - if (query < getLineStart(first_visible_line)) - { - return false; - } - - S32 current_line = first_visible_line; - S32 current_line_start, current_line_end; - for (;;) - { - current_line_start = getLineStart(current_line); - current_line_end = getLineStart(current_line + 1); - if (query >= current_line_start && query < current_line_end) - { - break; - } - if (current_line_start == current_line_end) - { - // We have reached on the last line. The query position must be here. - break; - } - current_line++; - } - - const LLWString textString(getWText()); - const llwchar * const text = textString.c_str(); - const S32 line_height = mFont->getLineHeight(); - - if (coord) - { - const S32 query_x = mVisibleTextRect.mLeft + mFont->getWidth(text, current_line_start, query - current_line_start); - const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; - S32 query_screen_x, query_screen_y; - localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); - LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); - } - - if (bounds) - { - S32 preedit_left = mVisibleTextRect.mLeft; - if (preedit_left_position > current_line_start) - { - preedit_left += mFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); - } - - S32 preedit_right = mVisibleTextRect.mLeft; - if (preedit_right_position < current_line_end) - { - preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); - } - else - { - preedit_right += mFont->getWidth(text, current_line_start, current_line_end - current_line_start); - } - - const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height; - const S32 preedit_bottom = preedit_top - line_height; - - const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom); - LLRect preedit_rect_screen; - localRectToScreen(preedit_rect_local, &preedit_rect_screen); - LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); - } - - return true; -} - -void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const -{ - if (hasSelection()) - { - *position = llmin(mSelectionStart, mSelectionEnd); - *length = llabs(mSelectionStart - mSelectionEnd); - } - else - { - *position = mCursorPos; - *length = 0; - } -} - -void LLTextEditor::getPreeditRange(S32 *position, S32 *length) const -{ - if (hasPreeditString()) - { - *position = mPreeditPositions.front(); - *length = mPreeditPositions.back() - mPreeditPositions.front(); - } - else - { - *position = mCursorPos; - *length = 0; - } -} - -void LLTextEditor::markAsPreedit(S32 position, S32 length) -{ - deselect(); - setCursorPos(position); - if (hasPreeditString()) - { - LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL; - } - mPreeditWString = LLWString( getWText(), position, length ); - if (length > 0) - { - mPreeditPositions.resize(2); - mPreeditPositions[0] = position; - mPreeditPositions[1] = position + length; - mPreeditStandouts.resize(1); - mPreeditStandouts[0] = false; - } - else - { - mPreeditPositions.clear(); - mPreeditStandouts.clear(); - } - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - mPreeditOverwrittenWString = mPreeditWString; - } - else - { - mPreeditOverwrittenWString.clear(); - } -} - -S32 LLTextEditor::getPreeditFontSize() const -{ - return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); -} - -bool LLTextEditor::isDirty() const -{ - if(mReadOnly) - { - return false; - } - - if( mPristineCmd ) - { - return ( mPristineCmd == mLastCmd ); - } - else - { - return ( NULL != mLastCmd ); - } -} - -void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback) -{ - mKeystrokeSignal.connect(callback); -} - -void LLTextEditor::onKeyStroke() -{ - mKeystrokeSignal(this); - - mSpellCheckStart = mSpellCheckEnd = -1; - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); -} - -//virtual -void LLTextEditor::clear() -{ - getViewModel()->setDisplay(LLWStringUtil::null); - clearSegments(); -} - -bool LLTextEditor::canLoadOrSaveToFile() -{ - return !mReadOnly; -} - -S32 LLTextEditor::spacesPerTab() -{ - return SPACES_PER_TAB; -} +/** + * @file lltexteditor.cpp + * + * $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$ + */ + +// Text editor widget to let users enter a a multi-line ASCII document. + +#include "linden_common.h" + +#define LLTEXTEDITOR_CPP +#include "lltexteditor.h" + +#include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR +#include "llfontgl.h" +#include "llgl.h" // LLGLSUIDefault() +#include "lllocalcliprect.h" +#include "llrender.h" +#include "llui.h" +#include "lluictrlfactory.h" +#include "llrect.h" +#include "llfocusmgr.h" +#include "lltimer.h" +#include "llmath.h" + +#include "llclipboard.h" +#include "llemojihelper.h" +#include "llscrollbar.h" +#include "llstl.h" +#include "llstring.h" +#include "llkeyboard.h" +#include "llkeywords.h" +#include "llundo.h" +#include "llviewborder.h" +#include "llcontrol.h" +#include "llwindow.h" +#include "lltextparser.h" +#include "llscrollcontainer.h" +#include "llspellcheck.h" +#include "llpanel.h" +#include "llurlregistry.h" +#include "lltooltip.h" +#include "llmenugl.h" + +#include +#include "llcombobox.h" + +// +// Globals +// +static LLDefaultChildRegistry::Register r("simple_text_editor"); + +// Compiler optimization, generate extern template +template class LLTextEditor* LLView::getChild( + const std::string& name, bool recurse) const; + +// +// Constants +// +const S32 SPACES_PER_TAB = 4; +const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on + +/////////////////////////////////////////////////////////////////// + +class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd +{ +public: + TextCmdInsert(S32 pos, bool group_with_next, const LLWString &ws, LLTextSegmentPtr segment) + : TextCmd(pos, group_with_next, segment), mWString(ws) + { + } + virtual ~TextCmdInsert() {} + virtual bool execute( LLTextBase* editor, S32* delta ) + { + *delta = insert(editor, getPosition(), mWString ); + LLWStringUtil::truncate(mWString, *delta); + //mWString = wstring_truncate(mWString, *delta); + return (*delta != 0); + } + virtual S32 undo( LLTextBase* editor ) + { + remove(editor, getPosition(), mWString.length() ); + return getPosition(); + } + virtual S32 redo( LLTextBase* editor ) + { + insert(editor, getPosition(), mWString ); + return getPosition() + mWString.length(); + } + +private: + LLWString mWString; +}; + +/////////////////////////////////////////////////////////////////// +class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd +{ +public: + TextCmdAddChar( S32 pos, bool group_with_next, llwchar wc, LLTextSegmentPtr segment) + : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(false) + { + } + virtual void blockExtensions() + { + mBlockExtensions = true; + } + virtual bool canExtend(S32 pos) const + { + // cannot extend text with custom segments + if (!mSegments.empty()) return false; + + return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length()); + } + virtual bool execute( LLTextBase* editor, S32* delta ) + { + *delta = insert(editor, getPosition(), mWString); + LLWStringUtil::truncate(mWString, *delta); + //mWString = wstring_truncate(mWString, *delta); + return (*delta != 0); + } + virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta ) + { + LLWString ws; + ws += wc; + + *delta = insert(editor, pos, ws); + if( *delta > 0 ) + { + mWString += wc; + } + return (*delta != 0); + } + virtual S32 undo( LLTextBase* editor ) + { + remove(editor, getPosition(), mWString.length() ); + return getPosition(); + } + virtual S32 redo( LLTextBase* editor ) + { + insert(editor, getPosition(), mWString ); + return getPosition() + mWString.length(); + } + +private: + LLWString mWString; + bool mBlockExtensions; + +}; + +/////////////////////////////////////////////////////////////////// + +class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd +{ +public: + TextCmdOverwriteChar( S32 pos, bool group_with_next, llwchar wc) + : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} + + virtual bool execute( LLTextBase* editor, S32* delta ) + { + mOldChar = editor->getWText()[getPosition()]; + overwrite(editor, getPosition(), mChar); + *delta = 0; + return true; + } + virtual S32 undo( LLTextBase* editor ) + { + overwrite(editor, getPosition(), mOldChar); + return getPosition(); + } + virtual S32 redo( LLTextBase* editor ) + { + overwrite(editor, getPosition(), mChar); + return getPosition()+1; + } + +private: + llwchar mChar; + llwchar mOldChar; +}; + +/////////////////////////////////////////////////////////////////// + +class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd +{ +public: + TextCmdRemove( S32 pos, bool group_with_next, S32 len, segment_vec_t& segments ) : + TextCmd(pos, group_with_next), mLen(len) + { + std::swap(mSegments, segments); + } + virtual bool execute( LLTextBase* editor, S32* delta ) + { + mWString = editor->getWText().substr(getPosition(), mLen); + *delta = remove(editor, getPosition(), mLen ); + return (*delta != 0); + } + virtual S32 undo( LLTextBase* editor ) + { + insert(editor, getPosition(), mWString); + return getPosition() + mWString.length(); + } + virtual S32 redo( LLTextBase* editor ) + { + remove(editor, getPosition(), mLen ); + return getPosition(); + } +private: + LLWString mWString; + S32 mLen; +}; + + +/////////////////////////////////////////////////////////////////// +LLTextEditor::Params::Params() +: default_text("default_text"), + prevalidator("prevalidator"), + embedded_items("embedded_items", false), + ignore_tab("ignore_tab", true), + auto_indent("auto_indent", true), + default_color("default_color"), + commit_on_focus_lost("commit_on_focus_lost", false), + show_context_menu("show_context_menu"), + show_emoji_helper("show_emoji_helper"), + enable_tooltip_paste("enable_tooltip_paste") +{ + addSynonym(prevalidator, "prevalidate_callback"); + addSynonym(prevalidator, "text_type"); +} + +LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : + LLTextBase(p), + mAutoreplaceCallback(), + mBaseDocIsPristine(true), + mPristineCmd( NULL ), + mLastCmd( NULL ), + mDefaultColor( p.default_color() ), + mAutoIndent(p.auto_indent), + mParseOnTheFly(false), + mCommitOnFocusLost( p.commit_on_focus_lost), + mAllowEmbeddedItems( p.embedded_items ), + mMouseDownX(0), + mMouseDownY(0), + mTabsToNextField(p.ignore_tab), + mPrevalidator(p.prevalidator()), + mShowContextMenu(p.show_context_menu), + mShowEmojiHelper(p.show_emoji_helper), + mEnableTooltipPaste(p.enable_tooltip_paste), + mPassDelete(false), + mKeepSelectionOnReturn(false) +{ + mSourceID.generate(); + + //FIXME: use image? + LLViewBorder::Params params; + params.name = "text ed border"; + params.rect = getLocalRect(); + params.bevel_style = LLViewBorder::BEVEL_IN; + params.border_thickness = 1; + params.visible = p.border_visible; + mBorder = LLUICtrlFactory::create (params); + addChild( mBorder ); + setText(p.default_text()); + + mParseOnTheFly = true; +} + +void LLTextEditor::initFromParams( const LLTextEditor::Params& p) +{ + LLTextBase::initFromParams(p); + + // HACK: text editors always need to be enabled so that we can scroll + LLView::setEnabled(true); + + if (p.commit_on_focus_lost.isProvided()) + { + mCommitOnFocusLost = p.commit_on_focus_lost; + } + + updateAllowingLanguageInput(); +} + +LLTextEditor::~LLTextEditor() +{ + gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid + + // Scrollbar is deleted by LLView + std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); + mUndoStack.clear(); + // Mark the menu as dead or its retained in memory till shutdown. + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if(menu) + { + menu->die(); + mContextMenuHandle.markDead(); + } +} + +//////////////////////////////////////////////////////////// +// LLTextEditor +// Public methods + +void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params) +{ + // validate incoming text if necessary + if (mPrevalidator) + { + if (!mPrevalidator.validate(utf8str)) + { + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + + // not valid text, nothing to do + return; + } + } + + blockUndo(); + deselect(); + + mParseOnTheFly = false; + LLTextBase::setText(utf8str, input_params); + mParseOnTheFly = true; + + resetDirty(); +} + +void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap) +{ + if (search_text_in.empty()) + { + return; + } + + LLWString text = getWText(); + LLWString search_text = utf8str_to_wstring(search_text_in); + if (case_insensitive) + { + LLWStringUtil::toLower(text); + LLWStringUtil::toLower(search_text); + } + + if (mIsSelecting) + { + LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd); + + if (selected_text == search_text) + { + // We already have this word selected, we are searching for the next. + setCursorPos(mCursorPos + search_text.size()); + } + } + + S32 loc = text.find(search_text,mCursorPos); + + // If Maybe we wrapped, search again + if (wrap && (-1 == loc)) + { + loc = text.find(search_text); + } + + // If still -1, then search_text just isn't found. + if (-1 == loc) + { + mIsSelecting = false; + mSelectionEnd = 0; + mSelectionStart = 0; + return; + } + + setCursorPos(loc); + + mIsSelecting = true; + mSelectionEnd = mCursorPos; + mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size())); +} + +bool LLTextEditor::replaceText(const std::string& search_text_in, const std::string& replace_text, + bool case_insensitive, bool wrap) +{ + bool replaced = false; + + if (search_text_in.empty()) + { + return replaced; + } + + LLWString search_text = utf8str_to_wstring(search_text_in); + if (mIsSelecting) + { + LLWString text = getWText(); + LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd); + + if (case_insensitive) + { + LLWStringUtil::toLower(selected_text); + LLWStringUtil::toLower(search_text); + } + + if (selected_text == search_text) + { + insertText(replace_text); + replaced = true; + } + } + + selectNext(search_text_in, case_insensitive, wrap); + return replaced; +} + +void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive) +{ + startOfDoc(); + selectNext(search_text, case_insensitive, false); + + bool replaced = true; + while ( replaced ) + { + replaced = replaceText(search_text,replace_text, case_insensitive, false); + } +} + +S32 LLTextEditor::prevWordPos(S32 cursorPos) const +{ + LLWString wtext(getWText()); + while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') ) + { + cursorPos--; + } + while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) + { + cursorPos--; + } + return cursorPos; +} + +S32 LLTextEditor::nextWordPos(S32 cursorPos) const +{ + LLWString wtext(getWText()); + while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) + { + cursorPos++; + } + while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') ) + { + cursorPos++; + } + return cursorPos; +} + +const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const +{ + static LLPointer index_segment = new LLIndexSegment; + + index_segment->setStart(mCursorPos); + index_segment->setEnd(mCursorPos); + + // find segment index at character to left of cursor (or rightmost edge of selection) + segment_set_t::const_iterator it = mSegments.lower_bound(index_segment); + + if (it != mSegments.end()) + { + return *it; + } + else + { + return LLTextSegmentPtr(); + } +} + +void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const +{ + S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos; + S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos; + + return getSegmentsInRange(segments, left, right, true); +} + +void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const +{ + segment_set_t::const_iterator first_it = getSegIterContaining(start); + segment_set_t::const_iterator end_it = getSegIterContaining(end - 1); + if (end_it != mSegments.end()) ++end_it; + + for (segment_set_t::const_iterator it = first_it; it != end_it; ++it) + { + LLTextSegmentPtr segment = *it; + if (include_partial + || (segment->getStart() >= start + && segment->getEnd() <= end)) + { + segments_out.push_back(segment); + } + } +} + +void LLTextEditor::setShowEmojiHelper(bool show) +{ + if (!mShowEmojiHelper) + { + LLEmojiHelper::instance().hideHelper(this); + } + + mShowEmojiHelper = show; +} + +bool LLTextEditor::selectionContainsLineBreaks() +{ + if (hasSelection()) + { + S32 left = llmin(mSelectionStart, mSelectionEnd); + S32 right = left + llabs(mSelectionStart - mSelectionEnd); + + LLWString wtext = getWText(); + for( S32 i = left; i < right; i++ ) + { + if (wtext[i] == '\n') + { + return true; + } + } + } + return false; +} + + +S32 LLTextEditor::indentLine( S32 pos, S32 spaces ) +{ + // Assumes that pos is at the start of the line + // spaces may be positive (indent) or negative (unindent). + // Returns the actual number of characters added or removed. + + llassert(pos >= 0); + llassert(pos <= getLength() ); + + S32 delta_spaces = 0; + + if (spaces >= 0) + { + // Indent + for(S32 i=0; i < spaces; i++) + { + delta_spaces += addChar(pos, ' '); + } + } + else + { + // Unindent + for(S32 i=0; i < -spaces; i++) + { + LLWString wtext = getWText(); + if (wtext[pos] == ' ') + { + delta_spaces += remove( pos, 1, false ); + } + } + } + + return delta_spaces; +} + +void LLTextEditor::indentSelectedLines( S32 spaces ) +{ + if( hasSelection() ) + { + LLWString text = getWText(); + S32 left = llmin( mSelectionStart, mSelectionEnd ); + S32 right = left + llabs( mSelectionStart - mSelectionEnd ); + bool cursor_on_right = (mSelectionEnd > mSelectionStart); + S32 cur = left; + + // Expand left to start of line + while( (cur > 0) && (text[cur] != '\n') ) + { + cur--; + } + left = cur; + if( cur > 0 ) + { + left++; + } + + // Expand right to end of line + if( text[right - 1] == '\n' ) + { + right--; + } + else + { + while( (text[right] != '\n') && (right <= getLength() ) ) + { + right++; + } + } + + // Disabling parsing on the fly to avoid updating text segments + // until all indentation commands are executed. + mParseOnTheFly = false; + + // Find each start-of-line and indent it + do + { + if( text[cur] == '\n' ) + { + cur++; + } + + S32 delta_spaces = indentLine( cur, spaces ); + if( delta_spaces > 0 ) + { + cur += delta_spaces; + } + right += delta_spaces; + + text = getWText(); + + // Find the next new line + while( (cur < right) && (text[cur] != '\n') ) + { + cur++; + } + } + while( cur < right ); + + mParseOnTheFly = true; + + if( (right < getLength()) && (text[right] == '\n') ) + { + right++; + } + + // Set the selection and cursor + if( cursor_on_right ) + { + mSelectionStart = left; + mSelectionEnd = right; + } + else + { + mSelectionStart = right; + mSelectionEnd = left; + } + setCursorPos(mSelectionEnd); + } +} + +//virtual +bool LLTextEditor::canSelectAll() const +{ + return true; +} + +// virtual +void LLTextEditor::selectAll() +{ + mSelectionStart = getLength(); + mSelectionEnd = 0; + setCursorPos(mSelectionEnd); + updatePrimary(); +} + +void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos) +{ + setCursorPos(prev_cursor_pos); + startSelection(); + setCursorPos(next_cursor_pos); + endSelection(); +} + +void LLTextEditor::insertEmoji(llwchar emoji) +{ + LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL; + auto styleParams = LLStyle::Params(); + styleParams.font = LLFontGL::getFontEmojiLarge(); + auto segment = new LLEmojiTextSegment(new LLStyle(styleParams), mCursorPos, mCursorPos + 1, *this); + insert(mCursorPos, LLWString(1, emoji), false, segment); + setCursorPos(mCursorPos + 1); +} + +void LLTextEditor::handleEmojiCommit(llwchar emoji) +{ + S32 shortCodePos; + if (LLEmojiHelper::isCursorInEmojiCode(getWText(), mCursorPos, &shortCodePos)) + { + remove(shortCodePos, mCursorPos - shortCodePos, true); + setCursorPos(shortCodePos); + + insertEmoji(emoji); + } +} + +bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // set focus first, in case click callbacks want to change it + // RN: do we really need to have a tab stop? + if (hasTabStop()) + { + setFocus( true ); + } + + // Let scrollbar have first dibs + handled = LLTextBase::handleMouseDown(x, y, mask); + + if( !handled ) + { + if (!(mask & MASK_SHIFT)) + { + deselect(); + } + + bool start_select = true; + if( start_select ) + { + // If we're not scrolling (handled by child), then we're selecting + if (mask & MASK_SHIFT) + { + S32 old_cursor_pos = mCursorPos; + setCursorAtLocalPos( x, y, true ); + + if (hasSelection()) + { + mSelectionEnd = mCursorPos; + } + else + { + mSelectionStart = old_cursor_pos; + mSelectionEnd = mCursorPos; + } + // assume we're starting a drag select + mIsSelecting = true; + } + else + { + setCursorAtLocalPos( x, y, true ); + startSelection(); + } + } + + handled = true; + } + + // Delay cursor flashing + resetCursorBlink(); + + if (handled && !gFocusMgr.getMouseCapture()) + { + gFocusMgr.setMouseCapture( this ); + } + return handled; +} + +bool LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (hasTabStop()) + { + setFocus(true); + } + + bool show_menu = false; + + // Prefer editor menu if it has selection. See EXT-6806. + if (hasSelection()) + { + S32 click_pos = getDocIndexFromLocalCoord(x, y, false); + if (click_pos > mSelectionStart && click_pos < mSelectionEnd) + { + show_menu = true; + } + } + + // Let segments handle the click, if nothing does, show editor menu + if (!show_menu && !LLTextBase::handleRightMouseDown(x, y, mask)) + { + show_menu = true; + } + + if (show_menu && getShowContextMenu()) + { + showContextMenu(x, y); + } + + return true; +} + + + +bool LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + if (hasTabStop()) + { + setFocus(true); + } + + if (!LLTextBase::handleMouseDown(x, y, mask)) + { + if( canPastePrimary() ) + { + setCursorAtLocalPos( x, y, true ); + // does not rely on focus being set + pastePrimary(); + } + } + return true; +} + + +bool LLTextEditor::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if(hasMouseCapture() ) + { + if( mIsSelecting ) + { + if(mScroller) + { + mScroller->autoScroll(x, y); + } + S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); + S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); + setCursorAtLocalPos( clamped_x, clamped_y, true ); + mSelectionEnd = mCursorPos; + } + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; + getWindow()->setCursor(UI_CURSOR_IBEAM); + handled = true; + } + + if( !handled ) + { + // Pass to children + handled = LLTextBase::handleHover(x, y, mask); + } + + if( handled ) + { + // Delay cursor flashing + resetCursorBlink(); + } + + if( !handled ) + { + getWindow()->setCursor(UI_CURSOR_IBEAM); + handled = true; + } + + return handled; +} + + +bool LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // if I'm not currently selecting text + if (!(mIsSelecting && hasMouseCapture())) + { + // let text segments handle mouse event + handled = LLTextBase::handleMouseUp(x, y, mask); + } + + if( !handled ) + { + if( mIsSelecting ) + { + if(mScroller) + { + mScroller->autoScroll(x, y); + } + S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); + S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); + setCursorAtLocalPos( clamped_x, clamped_y, true ); + endSelection(); + } + + // take selection to 'primary' clipboard + updatePrimary(); + + handled = true; + } + + // Delay cursor flashing + resetCursorBlink(); + + if( hasMouseCapture() ) + { + gFocusMgr.setMouseCapture( NULL ); + + handled = true; + } + + return handled; +} + + +bool LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // let scrollbar and text segments have first dibs + handled = LLTextBase::handleDoubleClick(x, y, mask); + + if( !handled ) + { + setCursorAtLocalPos( x, y, false ); + deselect(); + + LLWString text = getWText(); + + if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) ) + { + // Select word the cursor is over + while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1])) + { + if (!setCursorPos(mCursorPos - 1)) break; + } + startSelection(); + + while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) ) + { + if (!setCursorPos(mCursorPos + 1)) break; + } + + mSelectionEnd = mCursorPos; + } + else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) ) + { + // Select the character the cursor is over + startSelection(); + setCursorPos(mCursorPos + 1); + mSelectionEnd = mCursorPos; + } + + // We don't want handleMouseUp() to "finish" the selection (and thereby + // set mSelectionEnd to where the mouse is), so we finish the selection here. + mIsSelecting = false; + + // delay cursor flashing + resetCursorBlink(); + + // take selection to 'primary' clipboard + updatePrimary(); + + handled = true; + } + + return handled; +} + + +//---------------------------------------------------------------------------- +// Returns change in number of characters in mText + +S32 LLTextEditor::execute( TextCmd* cmd ) +{ + if (!mReadOnly && mShowEmojiHelper) + { + // Any change to our contents should always hide the helper + LLEmojiHelper::instance().hideHelper(this); + } + + S32 delta = 0; + if( cmd->execute(this, &delta) ) + { + // Delete top of undo stack + undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); + std::for_each(mUndoStack.begin(), enditer, DeletePointer()); + mUndoStack.erase(mUndoStack.begin(), enditer); + // Push the new command is now on the top (front) of the undo stack. + mUndoStack.push_front(cmd); + mLastCmd = cmd; + + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(getViewModel()->getDisplay()); + if (need_to_rollback) + { + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + + // get rid of this last command and clean up undo stack + undo(); + + // remove any evidence of this command from redo history + mUndoStack.pop_front(); + delete cmd; + + // failure, nothing changed + delta = 0; + } + } + else + { + // Operation failed, so don't put it on the undo stack. + delete cmd; + } + + return delta; +} + +S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) +{ + return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) ); +} + +S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) +{ + S32 end_pos = getEditableIndex(pos + length, true); + bool removedChar = false; + + segment_vec_t segments_to_remove; + // store text segments + getSegmentsInRange(segments_to_remove, pos, pos + length, false); + + if (pos <= end_pos) + { + removedChar = execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) ); + } + + return removedChar; +} + +S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) +{ + if ((S32)getLength() == pos) + { + return addChar(pos, wc); + } + else + { + return execute(new TextCmdOverwriteChar(pos, false, wc)); + } +} + +// Remove a single character from the text. Tries to remove +// a pseudo-tab (up to for spaces in a row) +void LLTextEditor::removeCharOrTab() +{ + if (!getEnabled()) + { + return; + } + + if (mCursorPos > 0) + { + S32 chars_to_remove = 1; + + LLWString text = getWText(); + if (text[mCursorPos - 1] == ' ') + { + // Try to remove a "tab" + S32 offset = getLineOffsetFromDocIndex(mCursorPos); + if (offset > 0) + { + chars_to_remove = offset % SPACES_PER_TAB; + if (chars_to_remove == 0) + { + chars_to_remove = SPACES_PER_TAB; + } + + for (S32 i = 0; i < chars_to_remove; i++) + { + if (text[mCursorPos - i - 1] != ' ') + { + // Fewer than a full tab's worth of spaces, so + // just delete a single character. + chars_to_remove = 1; + break; + } + } + } + } + + for (S32 i = 0; i < chars_to_remove; i++) + { + setCursorPos(mCursorPos - 1); + remove(mCursorPos, 1, false); + } + + tryToShowEmojiHelper(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } +} + +// Remove a single character from the text +S32 LLTextEditor::removeChar(S32 pos) +{ + return remove(pos, 1, false); +} + +void LLTextEditor::removeChar() +{ + if (!getEnabled()) + { + return; + } + + if (mCursorPos > 0) + { + setCursorPos(mCursorPos - 1); + removeChar(mCursorPos); + tryToShowEmojiHelper(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } +} + +// Add a single character to the text +S32 LLTextEditor::addChar(S32 pos, llwchar wc) +{ + if ((wstring_utf8_length(getWText()) + wchar_utf8_length(wc)) > mMaxTextByteLength) + { + LLUI::getInstance()->reportBadKeystroke(); + return 0; + } + + if (mLastCmd && mLastCmd->canExtend(pos)) + { + if (mPrevalidator) + { + // get a copy of current text contents + LLWString test_string(getViewModel()->getDisplay()); + + // modify text contents as if this addChar succeeded + llassert(pos <= (S32)test_string.size()); + test_string.insert(pos, 1, wc); + if (!mPrevalidator.validate(test_string)) + { + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + return 0; + } + } + + S32 delta = 0; + mLastCmd->extendAndExecute(this, pos, wc, &delta); + + return delta; + } + + return execute(new TextCmdAddChar(pos, false, wc, LLTextSegmentPtr())); +} + +void LLTextEditor::addChar(llwchar wc) +{ + if (!getEnabled()) + { + return; + } + + if (hasSelection()) + { + deleteSelection(true); + } + else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + removeChar(mCursorPos); + } + + setCursorPos(mCursorPos + addChar( mCursorPos, wc )); + tryToShowEmojiHelper(); + + if (!mReadOnly && mAutoreplaceCallback != NULL) + { + // autoreplace the text, if necessary + S32 replacement_start; + S32 replacement_length; + LLWString replacement_string; + S32 new_cursor_pos = mCursorPos; + mAutoreplaceCallback(replacement_start, replacement_length, replacement_string, new_cursor_pos, getWText()); + + if (replacement_length > 0 || !replacement_string.empty()) + { + remove(replacement_start, replacement_length, true); + insert(replacement_start, replacement_string, false, LLTextSegmentPtr()); + setCursorPos(new_cursor_pos); + } + } +} + +void LLTextEditor::showEmojiHelper() +{ + if (mReadOnly || !mShowEmojiHelper) + return; + + const LLRect cursorRect(getLocalRectFromDocIndex(mCursorPos)); + auto cb = [this](llwchar emoji) { insertEmoji(emoji); }; + LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb); +} + +void LLTextEditor::tryToShowEmojiHelper() +{ + if (mReadOnly || !mShowEmojiHelper) + return; + + S32 shortCodePos; + LLWString wtext(getWText()); + if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos)) + { + const LLRect cursorRect(getLocalRectFromDocIndex(shortCodePos)); + const LLWString wpart(wtext.substr(shortCodePos, mCursorPos - shortCodePos)); + const std::string part(wstring_to_utf8str(wpart)); + auto cb = [this](llwchar emoji) { handleEmojiCommit(emoji); }; + LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, part, cb); + } + else + { + LLEmojiHelper::instance().hideHelper(); + } +} + +void LLTextEditor::addLineBreakChar(bool group_together) +{ + if( !getEnabled() ) + { + return; + } + if( hasSelection() ) + { + deleteSelection(true); + } + else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + removeChar(mCursorPos); + } + + LLStyleConstSP sp(new LLStyle(LLStyle::Params())); + LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos); + + S32 pos = execute(new TextCmdAddChar(mCursorPos, group_together, '\n', segment)); + + setCursorPos(mCursorPos + pos); +} + + +bool LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) +{ + bool handled = false; + + if( mask & MASK_SHIFT ) + { + handled = true; + + switch( key ) + { + case KEY_LEFT: + if( 0 < mCursorPos ) + { + startSelection(); + setCursorPos(mCursorPos - 1); + if( mask & MASK_CONTROL ) + { + setCursorPos(prevWordPos(mCursorPos)); + } + mSelectionEnd = mCursorPos; + } + break; + + case KEY_RIGHT: + if( mCursorPos < getLength() ) + { + startSelection(); + setCursorPos(mCursorPos + 1); + if( mask & MASK_CONTROL ) + { + setCursorPos(nextWordPos(mCursorPos)); + } + mSelectionEnd = mCursorPos; + } + break; + + case KEY_UP: + startSelection(); + changeLine( -1 ); + mSelectionEnd = mCursorPos; + break; + + case KEY_PAGE_UP: + startSelection(); + changePage( -1 ); + mSelectionEnd = mCursorPos; + break; + + case KEY_HOME: + startSelection(); + if( mask & MASK_CONTROL ) + { + setCursorPos(0); + } + else + { + startOfLine(); + } + mSelectionEnd = mCursorPos; + break; + + case KEY_DOWN: + startSelection(); + changeLine( 1 ); + mSelectionEnd = mCursorPos; + break; + + case KEY_PAGE_DOWN: + startSelection(); + changePage( 1 ); + mSelectionEnd = mCursorPos; + break; + + case KEY_END: + startSelection(); + if( mask & MASK_CONTROL ) + { + setCursorPos(getLength()); + } + else + { + endOfLine(); + } + mSelectionEnd = mCursorPos; + break; + + default: + handled = false; + break; + } + } + + if( handled ) + { + // take selection to 'primary' clipboard + updatePrimary(); + } + + return handled; +} + +bool LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) +{ + bool handled = false; + + // Ignore capslock key + if( MASK_NONE == mask ) + { + handled = true; + switch( key ) + { + case KEY_UP: + changeLine( -1 ); + break; + + case KEY_PAGE_UP: + changePage( -1 ); + break; + + case KEY_HOME: + startOfLine(); + break; + + case KEY_DOWN: + changeLine( 1 ); + deselect(); + break; + + case KEY_PAGE_DOWN: + changePage( 1 ); + break; + + case KEY_END: + endOfLine(); + break; + + case KEY_LEFT: + if( hasSelection() ) + { + setCursorPos(llmin( mSelectionStart, mSelectionEnd )); + } + else + { + if( 0 < mCursorPos ) + { + setCursorPos(mCursorPos - 1); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + } + break; + + case KEY_RIGHT: + if( hasSelection() ) + { + setCursorPos(llmax( mSelectionStart, mSelectionEnd )); + } + else + { + if( mCursorPos < getLength() ) + { + setCursorPos(mCursorPos + 1); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + } + break; + + default: + handled = false; + break; + } + } + + if (handled) + { + deselect(); + } + + return handled; +} + +void LLTextEditor::deleteSelection(bool group_with_next_op ) +{ + if( getEnabled() && hasSelection() ) + { + S32 pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + + remove( pos, length, group_with_next_op ); + + deselect(); + setCursorPos(pos); + } +} + +// virtual +bool LLTextEditor::canCut() const +{ + return !mReadOnly && hasSelection(); +} + +// cut selection to clipboard +void LLTextEditor::cut() +{ + if( !canCut() ) + { + return; + } + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + LLClipboard::instance().copyToClipboard( getWText(), left_pos, length); + deleteSelection( false ); + + onKeyStroke(); +} + +bool LLTextEditor::canCopy() const +{ + return hasSelection(); +} + +// copy selection to clipboard +void LLTextEditor::copy() +{ + if( !canCopy() ) + { + return; + } + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + LLClipboard::instance().copyToClipboard(getWText(), left_pos, length); +} + +bool LLTextEditor::canPaste() const +{ + return !mReadOnly && LLClipboard::instance().isTextAvailable(); +} + +// paste from clipboard +void LLTextEditor::paste() +{ + bool is_primary = false; + pasteHelper(is_primary); +} + +// paste from primary +void LLTextEditor::pastePrimary() +{ + bool is_primary = true; + pasteHelper(is_primary); +} + +// paste from primary (itsprimary==true) or clipboard (itsprimary==false) +void LLTextEditor::pasteHelper(bool is_primary) +{ + struct BoolReset + { + BoolReset(bool& value) : mValuePtr(&value) { *mValuePtr = false; } + ~BoolReset() { *mValuePtr = true; } + bool* mValuePtr; + } reset(mParseOnTheFly); + + bool can_paste_it; + if (is_primary) + { + can_paste_it = canPastePrimary(); + } + else + { + can_paste_it = canPaste(); + } + + if (!can_paste_it) + { + return; + } + + LLWString paste; + LLClipboard::instance().pasteFromClipboard(paste, is_primary); + + if (paste.empty()) + { + return; + } + + // Delete any selected characters (the paste replaces them) + if( (!is_primary) && hasSelection() ) + { + deleteSelection(true); + } + + // Clean up string (replace tabs and remove characters that our fonts don't support). + LLWString clean_string(paste); + cleanStringForPaste(clean_string); + + // Insert the new text into the existing text. + + //paste text with linebreaks. + pasteTextWithLinebreaks(clean_string); + + deselect(); + + onKeyStroke(); +} + + +// Clean up string (replace tabs and remove characters that our fonts don't support). +void LLTextEditor::cleanStringForPaste(LLWString & clean_string) +{ + std::string clean_string_utf = wstring_to_utf8str(clean_string); + std::replace( clean_string_utf.begin(), clean_string_utf.end(), '\r', '\n'); + clean_string = utf8str_to_wstring(clean_string_utf); + + LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB); + if( mAllowEmbeddedItems ) + { + const llwchar LF = 10; + S32 len = clean_string.length(); + for( S32 i = 0; i < len; i++ ) + { + llwchar wc = clean_string[i]; + if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) ) + { + clean_string[i] = LL_UNKNOWN_CHAR; + } + else if (wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR) + { + clean_string[i] = pasteEmbeddedItem(wc); + } + } + } +} + + +void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string) +{ + std::basic_string::size_type start = 0; + std::basic_string::size_type pos = clean_string.find('\n',start); + + while((pos != -1) && (pos != clean_string.length() -1)) + { + if(pos!=start) + { + std::basic_string str = std::basic_string(clean_string,start,pos-start); + setCursorPos(mCursorPos + insert(mCursorPos, str, true, LLTextSegmentPtr())); + } + addLineBreakChar(true); // Add a line break and group with the next addition. + + start = pos+1; + pos = clean_string.find('\n',start); + } + + if (pos != start) + { + std::basic_string str = std::basic_string(clean_string,start,clean_string.length()-start); + setCursorPos(mCursorPos + insert(mCursorPos, str, false, LLTextSegmentPtr())); + } + else + { + addLineBreakChar(false); // Add a line break and end the grouping. + } +} + +// copy selection to primary +void LLTextEditor::copyPrimary() +{ + if( !canCopy() ) + { + return; + } + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true); +} + +bool LLTextEditor::canPastePrimary() const +{ + return !mReadOnly && LLClipboard::instance().isTextAvailable(true); +} + +void LLTextEditor::updatePrimary() +{ + if (canCopy()) + { + copyPrimary(); + } +} + +bool LLTextEditor::handleControlKey(const KEY key, const MASK mask) +{ + bool handled = false; + + if( mask & MASK_CONTROL ) + { + handled = true; + + switch( key ) + { + case KEY_HOME: + if( mask & MASK_SHIFT ) + { + startSelection(); + setCursorPos(0); + mSelectionEnd = mCursorPos; + } + else + { + // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down + // all move the cursor as if clicking, so should deselect. + deselect(); + startOfDoc(); + } + break; + + case KEY_END: + { + if( mask & MASK_SHIFT ) + { + startSelection(); + } + else + { + // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down + // all move the cursor as if clicking, so should deselect. + deselect(); + } + endOfDoc(); + if( mask & MASK_SHIFT ) + { + mSelectionEnd = mCursorPos; + } + break; + } + + case KEY_RIGHT: + if( mCursorPos < getLength() ) + { + // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down + // all move the cursor as if clicking, so should deselect. + deselect(); + + setCursorPos(nextWordPos(mCursorPos + 1)); + } + break; + + + case KEY_LEFT: + if( mCursorPos > 0 ) + { + // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down + // all move the cursor as if clicking, so should deselect. + deselect(); + + setCursorPos(prevWordPos(mCursorPos - 1)); + } + break; + + default: + handled = false; + break; + } + } + + if (handled && !gFocusMgr.getMouseCapture()) + { + updatePrimary(); + } + + return handled; +} + + +bool LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) + { + bool handled = true; + + if (mReadOnly) return false; + + switch( key ) + { + case KEY_INSERT: + if (mask == MASK_NONE) + { + gKeyboard->toggleInsertMode(); + } + break; + + case KEY_BACKSPACE: + if( hasSelection() ) + { + deleteSelection(false); + } + else + if( 0 < mCursorPos ) + { + removeCharOrTab(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + break; + + + case KEY_RETURN: + if (mask == MASK_NONE) + { + if( hasSelection() && !mKeepSelectionOnReturn ) + { + deleteSelection(false); + } + if (mAutoIndent) + { + autoIndent(); + } + } + else + { + handled = false; + break; + } + break; + + case KEY_TAB: + if (mask & MASK_CONTROL) + { + handled = false; + break; + } + if( hasSelection() && selectionContainsLineBreaks() ) + { + indentSelectedLines( (mask & MASK_SHIFT) ? -SPACES_PER_TAB : SPACES_PER_TAB ); + } + else + { + if( hasSelection() ) + { + deleteSelection(false); + } + + S32 offset = getLineOffsetFromDocIndex(mCursorPos); + + S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB); + for( S32 i=0; i < spaces_needed; i++ ) + { + addChar( ' ' ); + } + } + break; + + default: + handled = false; + break; + } + + if (handled) + { + onKeyStroke(); + } + return handled; +} + + +void LLTextEditor::unindentLineBeforeCloseBrace() +{ + if( mCursorPos >= 1 ) + { + LLWString text = getWText(); + if( ' ' == text[ mCursorPos - 1 ] ) + { + S32 line = getLineNumFromDocIndex(mCursorPos, false); + S32 line_start = getLineStart(line); + + // Jump over spaces in the current line + while ((' ' == text[line_start]) && (line_start < mCursorPos)) + { + line_start++; + } + + // Make sure there is nothing but ' ' before the Brace we are unindenting + if (line_start == mCursorPos) + { + removeCharOrTab(); + } + } + } +} + + +bool LLTextEditor::handleKeyHere(KEY key, MASK mask ) +{ + bool handled = false; + + // Special case for TAB. If want to move to next field, report + // not handled and let the parent take care of field movement. + if (KEY_TAB == key && mTabsToNextField) + { + return false; + } + + if (mReadOnly && mScroller) + { + handled = (mScroller && mScroller->handleKeyHere( key, mask )) + || handleSelectionKey(key, mask) + || handleControlKey(key, mask); + } + else + { + if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask)) + { + return true; + } + + if (mEnableTooltipPaste && + LLToolTipMgr::instance().toolTipVisible() && + LLToolTipMgr::instance().isTooltipPastable() && + KEY_TAB == key) + { // Paste the first line of a tooltip into the editor + std::string message; + LLToolTipMgr::instance().getToolTipMessage(message); + LLWString tool_tip_text(utf8str_to_wstring(message)); + + if (tool_tip_text.size() > 0) + { + // Delete any selected characters (the tooltip text replaces them) + if(hasSelection()) + { + deleteSelection(true); + } + + std::basic_string::size_type pos = tool_tip_text.find('\n',0); + if (pos != -1) + { // Extract the first line of the tooltip + tool_tip_text = std::basic_string(tool_tip_text, 0, pos); + } + + // Add the text + cleanStringForPaste(tool_tip_text); + pasteTextWithLinebreaks(tool_tip_text); + handled = true; + } + } + else + { // Normal key handling + handled = handleNavigationKey( key, mask ) + || handleSelectionKey(key, mask) + || handleControlKey(key, mask) + || handleSpecialKey(key, mask); + } + } + + if( handled ) + { + resetCursorBlink(); + needsScroll(); + + if (mShowEmojiHelper) + { + // Dismiss the helper whenever we handled a key that it didn't + LLEmojiHelper::instance().hideHelper(this); + } + } + + return handled; +} + + +bool LLTextEditor::handleUnicodeCharHere(llwchar uni_char) +{ + if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL + { + return false; + } + + bool handled = false; + + // Handle most keys only if the text editor is writeable. + if( !mReadOnly ) + { + if (mShowEmojiHelper && uni_char < 0x80 && LLEmojiHelper::instance().handleKey(this, (KEY)uni_char, MASK_NONE)) + { + return true; + } + + if( mAutoIndent && '}' == uni_char ) + { + unindentLineBeforeCloseBrace(); + } + + // TODO: KLW Add auto show of tool tip on ( + addChar( uni_char ); + + // Keys that add characters temporarily hide the cursor + getWindow()->hideCursorUntilMouseMove(); + + handled = true; + } + + if( handled ) + { + resetCursorBlink(); + + // Most keystrokes will make the selection box go away, but not all will. + deselect(); + + onKeyStroke(); + } + + return handled; +} + + +// virtual +bool LLTextEditor::canDoDelete() const +{ + return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) ); +} + +void LLTextEditor::doDelete() +{ + if( !canDoDelete() ) + { + return; + } + if( hasSelection() ) + { + deleteSelection(false); + } + else + if( mCursorPos < getLength() ) + { + S32 i; + S32 chars_to_remove = 1; + LLWString text = getWText(); + if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) ) + { + // Try to remove a full tab's worth of spaces + S32 offset = getLineOffsetFromDocIndex(mCursorPos); + chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB); + if( chars_to_remove == 0 ) + { + chars_to_remove = SPACES_PER_TAB; + } + + for( i = 0; i < chars_to_remove; i++ ) + { + if( text[mCursorPos + i] != ' ' ) + { + chars_to_remove = 1; + break; + } + } + } + + for( i = 0; i < chars_to_remove; i++ ) + { + setCursorPos(mCursorPos + 1); + removeChar(); + } + + } + + onKeyStroke(); +} + +//---------------------------------------------------------------------------- + + +void LLTextEditor::blockUndo() +{ + mBaseDocIsPristine = false; + mLastCmd = NULL; + std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); + mUndoStack.clear(); +} + +// virtual +bool LLTextEditor::canUndo() const +{ + return !mReadOnly && mLastCmd != NULL; +} + +void LLTextEditor::undo() +{ + if( !canUndo() ) + { + return; + } + deselect(); + S32 pos = 0; + do + { + pos = mLastCmd->undo(this); + undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); + if (iter != mUndoStack.end()) + ++iter; + if (iter != mUndoStack.end()) + mLastCmd = *iter; + else + mLastCmd = NULL; + + } while( mLastCmd && mLastCmd->groupWithNext() ); + + setCursorPos(pos); + + onKeyStroke(); +} + +bool LLTextEditor::canRedo() const +{ + return !mReadOnly && (mUndoStack.size() > 0) && (mLastCmd != mUndoStack.front()); +} + +void LLTextEditor::redo() +{ + if( !canRedo() ) + { + return; + } + deselect(); + S32 pos = 0; + do + { + if( !mLastCmd ) + { + mLastCmd = mUndoStack.back(); + } + else + { + undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); + if (iter != mUndoStack.begin()) + mLastCmd = *(--iter); + else + mLastCmd = NULL; + } + + if( mLastCmd ) + { + pos = mLastCmd->redo(this); + } + } while( + mLastCmd && + mLastCmd->groupWithNext() && + (mLastCmd != mUndoStack.front()) ); + + setCursorPos(pos); + + onKeyStroke(); +} + +void LLTextEditor::onFocusReceived() +{ + LLTextBase::onFocusReceived(); + updateAllowingLanguageInput(); +} + +void LLTextEditor::focusLostHelper() +{ + updateAllowingLanguageInput(); + + // Route menu back to the default + if( gEditMenuHandler == this ) + { + gEditMenuHandler = NULL; + } + + if (mCommitOnFocusLost) + { + onCommit(); + } + + // Make sure cursor is shown again + getWindow()->showCursorFromMouseMove(); +} + +void LLTextEditor::onFocusLost() +{ + focusLostHelper(); + LLTextBase::onFocusLost(); +} + +void LLTextEditor::onCommit() +{ + setControlValue(getValue()); + LLTextBase::onCommit(); +} + +void LLTextEditor::setEnabled(bool enabled) +{ + // just treat enabled as read-only flag + bool read_only = !enabled; + if (read_only != mReadOnly) + { + //mReadOnly = read_only; + LLTextBase::setReadOnly(read_only); + updateSegments(); + updateAllowingLanguageInput(); + } +} + +void LLTextEditor::showContextMenu(S32 x, S32 y) +{ + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (!menu) + { + llassert(LLMenuGL::sMenuContainer != NULL); + menu = LLUICtrlFactory::createFromFile("menu_text_editor.xml", + LLMenuGL::sMenuContainer, + LLMenuHolderGL::child_registry_t::instance()); + if(!menu) + { + LL_WARNS() << "Failed to create menu for LLTextEditor: " << getName() << LL_ENDL; + return; + } + mContextMenuHandle = menu->getHandle(); + } + + // Route menu to this class + // previously this was done in ::handleRightMoseDown: + //if(hasTabStop()) + // setFocus(true) - why? weird... + // and then inside setFocus + // .... + // gEditMenuHandler = this; + // .... + // but this didn't work in all cases and just weird... + //why not here? + // (all this was done for EXT-4443) + + gEditMenuHandler = this; + + S32 screen_x, screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + setCursorAtLocalPos(x, y, false); + if (hasSelection()) + { + if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) ) + { + deselect(); + } + else + { + setCursorPos(llmax(mSelectionStart, mSelectionEnd)); + } + } + + bool use_spellcheck = getSpellCheck(), is_misspelled = false; + if (use_spellcheck) + { + mSuggestionList.clear(); + + // If the cursor is on a misspelled word, retrieve suggestions for it + std::string misspelled_word = getMisspelledWord(mCursorPos); + if ((is_misspelled = !misspelled_word.empty())) + { + LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); + } + } + + menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty())); + menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled)); + menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled)); + menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled)); + menu->show(screen_x, screen_y, this); +} + + +void LLTextEditor::drawPreeditMarker() +{ + static LLUICachedControl preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); + static LLUICachedControl preedit_marker_gap ("UIPreeditMarkerGap", 0); + static LLUICachedControl preedit_marker_position ("UIPreeditMarkerPosition", 0); + static LLUICachedControl preedit_marker_thickness ("UIPreeditMarkerThickness", 0); + static LLUICachedControl preedit_standout_brightness ("UIPreeditStandoutBrightness", 0); + static LLUICachedControl preedit_standout_gap ("UIPreeditStandoutGap", 0); + static LLUICachedControl preedit_standout_position ("UIPreeditStandoutPosition", 0); + static LLUICachedControl preedit_standout_thickness ("UIPreeditStandoutThickness", 0); + + if (!hasPreeditString()) + { + return; + } + + const LLWString textString(getWText()); + const llwchar *text = textString.c_str(); + const S32 text_len = getLength(); + const S32 num_lines = getLineCount(); + + S32 cur_line = getFirstVisibleLine(); + if (cur_line >= num_lines) + { + return; + } + + const S32 line_height = mFont->getLineHeight(); + + S32 line_start = getLineStart(cur_line); + S32 line_y = mVisibleTextRect.mTop - line_height; + while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line)) + { + S32 next_start = -1; + S32 line_end = text_len; + + if ((cur_line + 1) < num_lines) + { + next_start = getLineStart(cur_line + 1); + line_end = next_start; + } + if ( text[line_end-1] == '\n' ) + { + --line_end; + } + + // Does this line contain preedits? + if (line_start >= mPreeditPositions.back()) + { + // We have passed the preedits. + break; + } + if (line_end > mPreeditPositions.front()) + { + for (U32 i = 0; i < mPreeditStandouts.size(); i++) + { + S32 left = mPreeditPositions[i]; + S32 right = mPreeditPositions[i + 1]; + if (right <= line_start || left >= line_end) + { + continue; + } + + line_info& line = mLineInfoList[cur_line]; + LLRect text_rect(line.mRect); + text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents + text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position + + S32 preedit_left = text_rect.mLeft; + if (left > line_start) + { + preedit_left += mFont->getWidth(text, line_start, left - line_start); + } + S32 preedit_right = text_rect.mLeft; + if (right < line_end) + { + preedit_right += mFont->getWidth(text, line_start, right - line_start); + } + else + { + preedit_right += mFont->getWidth(text, line_start, line_end - line_start); + } + + if (mPreeditStandouts[i]) + { + gl_rect_2d(preedit_left + preedit_standout_gap, + text_rect.mBottom + mFont->getDescenderHeight() - 1, + preedit_right - preedit_standout_gap - 1, + text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_standout_thickness, + (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f)); + } + else + { + gl_rect_2d(preedit_left + preedit_marker_gap, + text_rect.mBottom + mFont->getDescenderHeight() - 1, + preedit_right - preedit_marker_gap - 1, + text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_marker_thickness, + (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f)); + } + } + } + + // move down one line + line_y -= line_height; + line_start = next_start; + cur_line++; + } +} + +void LLTextEditor::draw() +{ + { + // pad clipping rectangle so that cursor can draw at full width + // when at left edge of mVisibleTextRect + LLRect clip_rect(mVisibleTextRect); + clip_rect.stretch(1); + LLLocalClipRect clip(clip_rect); + } + + LLTextBase::draw(); + + drawPreeditMarker(); + + //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret + // when in readonly mode + mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly); +} + +// Start or stop the editor from accepting text-editing keystrokes +// see also LLLineEditor +void LLTextEditor::setFocus( bool new_state ) +{ + bool old_state = hasFocus(); + + // Don't change anything if the focus state didn't change + if (new_state == old_state) return; + + // Notify early if we are losing focus. + if (!new_state) + { + getWindow()->allowLanguageTextInput(this, false); + } + + LLTextBase::setFocus( new_state ); + + if( new_state ) + { + // Route menu to this class + gEditMenuHandler = this; + + // Don't start the cursor flashing right away + resetCursorBlink(); + } + else + { + // Route menu back to the default + if( gEditMenuHandler == this ) + { + gEditMenuHandler = NULL; + } + + endSelection(); + } +} + +// public +void LLTextEditor::setCursorAndScrollToEnd() +{ + deselect(); + endOfDoc(); +} + +void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap ) +{ + *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap); + *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap); +} + +void LLTextEditor::autoIndent() +{ + // Count the number of spaces in the current line + S32 line = getLineNumFromDocIndex(mCursorPos, false); + S32 line_start = getLineStart(line); + S32 space_count = 0; + S32 i; + + LLWString text = getWText(); + S32 offset = getLineOffsetFromDocIndex(mCursorPos); + while(( ' ' == text[line_start] ) && (space_count < offset)) + { + space_count++; + line_start++; + } + + // If we're starting a braced section, indent one level. + if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') ) + { + space_count += SPACES_PER_TAB; + } + + // Insert that number of spaces on the new line + + //appendLineBreakSegment(LLStyle::Params());//addChar( '\n' ); + addLineBreakChar(); + + for( i = 0; i < space_count; i++ ) + { + addChar( ' ' ); + } +} + +// Inserts new text at the cursor position +void LLTextEditor::insertText(const std::string &new_text) +{ + bool enabled = getEnabled(); + setEnabled( true ); + + // Delete any selected characters (the insertion replaces them) + if( hasSelection() ) + { + deleteSelection(true); + } + + setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), false, LLTextSegmentPtr() )); + + setEnabled( enabled ); +} + +void LLTextEditor::insertText(LLWString &new_text) +{ + bool enabled = getEnabled(); + setEnabled( true ); + + // Delete any selected characters (the insertion replaces them) + if( hasSelection() ) + { + deleteSelection(true); + } + + setCursorPos(mCursorPos + insert( mCursorPos, new_text, false, LLTextSegmentPtr() )); + + setEnabled( enabled ); +} + +void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo) +{ + // Save old state + S32 selection_start = mSelectionStart; + S32 selection_end = mSelectionEnd; + bool was_selecting = mIsSelecting; + S32 cursor_pos = mCursorPos; + S32 old_length = getLength(); + bool cursor_was_at_end = (mCursorPos == old_length); + + deselect(); + + setCursorPos(old_length); + + LLWString widget_wide_text = utf8str_to_wstring(text); + + LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size()); + insert(getLength(), widget_wide_text, false, segment); + + // Set the cursor and scroll position + if( selection_start != selection_end ) + { + mSelectionStart = selection_start; + mSelectionEnd = selection_end; + + mIsSelecting = was_selecting; + setCursorPos(cursor_pos); + } + else if( cursor_was_at_end ) + { + setCursorPos(getLength()); + } + else + { + setCursorPos(cursor_pos); + } + + if (!allow_undo) + { + blockUndo(); + } +} + +void LLTextEditor::removeTextFromEnd(S32 num_chars) +{ + if (num_chars <= 0) return; + + remove(getLength() - num_chars, num_chars, false); + + S32 len = getLength(); + setCursorPos (llclamp(mCursorPos, 0, len)); + mSelectionStart = llclamp(mSelectionStart, 0, len); + mSelectionEnd = llclamp(mSelectionEnd, 0, len); + + needsScroll(); +} + +//---------------------------------------------------------------------------- + +void LLTextEditor::onSpellCheckPerformed() +{ + if (isPristine()) + { + mBaseDocIsPristine = false; + } +} + +void LLTextEditor::makePristine() +{ + mPristineCmd = mLastCmd; + mBaseDocIsPristine = !mLastCmd; + + // Create a clean partition in the undo stack. We don't want a single command to extend from + // the "pre-pristine" state to the "post-pristine" state. + if( mLastCmd ) + { + mLastCmd->blockExtensions(); + } +} + +bool LLTextEditor::isPristine() const +{ + if( mPristineCmd ) + { + return (mPristineCmd == mLastCmd); + } + else + { + // No undo stack, so check if the version before and commands were done was the original version + return !mLastCmd && mBaseDocIsPristine; + } +} + +bool LLTextEditor::tryToRevertToPristineState() +{ + if( !isPristine() ) + { + deselect(); + S32 i = 0; + while( !isPristine() && canUndo() ) + { + undo(); + i--; + } + + while( !isPristine() && canRedo() ) + { + redo(); + i++; + } + + if( !isPristine() ) + { + // failed, so go back to where we started + while( i > 0 ) + { + undo(); + i--; + } + } + } + + return isPristine(); // true => success +} + +void LLTextEditor::updateLinkSegments() +{ + LLWString wtext = getWText(); + + // update any segments that contain a link + for (segment_set_t::iterator it = mSegments.begin(); it != mSegments.end(); ++it) + { + LLTextSegment *segment = *it; + if (segment && segment->getStyle() && segment->getStyle()->isLink()) + { + LLStyleConstSP style = segment->getStyle(); + LLStyleSP new_style(new LLStyle(*style)); + LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart()); + + segment_set_t::const_iterator next_it = mSegments.upper_bound(segment); + LLTextSegment *next_segment = *next_it; + if (next_segment) + { + LLWString next_url_label = wtext.substr(next_segment->getStart(), next_segment->getEnd()-next_segment->getStart()); + std::string link_check = wstring_to_utf8str(url_label) + wstring_to_utf8str(next_url_label); + LLUrlMatch match; + + if ( LLUrlRegistry::instance().findUrl(link_check, match)) + { + if(match.getQuery() == wstring_to_utf8str(next_url_label)) + { + continue; + } + } + } + + // if the link's label (what the user can edit) is a valid Url, + // then update the link's HREF to be the same as the label text. + // This lets users edit Urls in-place. + if (acceptsTextInput() && LLUrlRegistry::instance().hasUrl(url_label)) + { + std::string new_url = wstring_to_utf8str(url_label); + LLStringUtil::trim(new_url); + new_style->setLinkHREF(new_url); + LLStyleConstSP sp(new_style); + segment->setStyle(sp); + } + } + } +} + + + +void LLTextEditor::onMouseCaptureLost() +{ + endSelection(); +} + +/////////////////////////////////////////////////////////////////// +// Hack for Notecards + +bool LLTextEditor::importBuffer(const char* buffer, S32 length ) +{ + std::istringstream instream(buffer); + + // Version 1 format: + // Linden text version 1\n + // {\n + // + // Text length \n + // (text may contain ext_char_values) + // }\n + + char tbuf[MAX_STRING]; /* Flawfinder: ignore */ + + S32 version = 0; + instream.getline(tbuf, MAX_STRING); + if( 1 != sscanf(tbuf, "Linden text version %d", &version) ) + { + LL_WARNS() << "Invalid Linden text file header " << LL_ENDL; + return false; + } + + if( 1 != version ) + { + LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL; + return false; + } + + instream.getline(tbuf, MAX_STRING); + if( 0 != sscanf(tbuf, "{") ) + { + LL_WARNS() << "Invalid Linden text file format" << LL_ENDL; + return false; + } + + S32 text_len = 0; + instream.getline(tbuf, MAX_STRING); + if( 1 != sscanf(tbuf, "Text length %d", &text_len) ) + { + LL_WARNS() << "Invalid Linden text length field" << LL_ENDL; + return false; + } + + if( text_len > mMaxTextByteLength ) + { + LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL; + return false; + } + + bool success = true; + + char* text = new char[ text_len + 1]; + if (text == NULL) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS() << "Memory allocation failure." << LL_ENDL; + return false; + } + instream.get(text, text_len + 1, '\0'); + text[text_len] = '\0'; + if( text_len != (S32)strlen(text) )/* Flawfinder: ignore */ + { + LL_WARNS() << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << LL_ENDL;/* Flawfinder: ignore */ + success = false; + } + + instream.getline(tbuf, MAX_STRING); + if( success && (0 != sscanf(tbuf, "}")) ) + { + LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL; + success = false; + } + + if( success ) + { + // Actually set the text + setText( LLStringExplicit(text) ); + } + + delete[] text; + + startOfDoc(); + deselect(); + + return success; +} + +bool LLTextEditor::exportBuffer(std::string &buffer ) +{ + std::ostringstream outstream(buffer); + + outstream << "Linden text version 1\n"; + outstream << "{\n"; + + outstream << llformat("Text length %d\n", getLength() ); + outstream << getText(); + outstream << "}\n"; + + return true; +} + +void LLTextEditor::updateAllowingLanguageInput() +{ + LLWindow* window = getWindow(); + if (!window) + { + // test app, no window available + return; + } + if (hasFocus() && !mReadOnly) + { + window->allowLanguageTextInput(this, true); + } + else + { + window->allowLanguageTextInput(this, false); + } +} + +// Preedit is managed off the undo/redo command stack. + +bool LLTextEditor::hasPreeditString() const +{ + return (mPreeditPositions.size() > 1); +} + +void LLTextEditor::resetPreedit() +{ + if (hasSelection()) + { + if (hasPreeditString()) + { + LL_WARNS() << "Preedit and selection!" << LL_ENDL; + deselect(); + } + else + { + deleteSelection(true); + } + } + if (hasPreeditString()) + { + if (hasSelection()) + { + LL_WARNS() << "Preedit and selection!" << LL_ENDL; + deselect(); + } + + setCursorPos(mPreeditPositions.front()); + removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos); + insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString); + + mPreeditWString.clear(); + mPreeditOverwrittenWString.clear(); + mPreeditPositions.clear(); + + // A call to updatePreedit should soon follow under a + // normal course of operation, so we don't need to + // maintain internal variables such as line start + // positions now. + } +} + +void LLTextEditor::updatePreedit(const LLWString &preedit_string, + const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) +{ + // Just in case. + if (mReadOnly) + { + return; + } + + getWindow()->hideCursorUntilMouseMove(); + + S32 insert_preedit_at = mCursorPos; + + mPreeditWString = preedit_string; + mPreeditPositions.resize(preedit_segment_lengths.size() + 1); + S32 position = insert_preedit_at; + for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) + { + mPreeditPositions[i] = position; + position += preedit_segment_lengths[i]; + } + mPreeditPositions.back() = position; + + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length()); + removeStringNoUndo(insert_preedit_at, mPreeditWString.length()); + } + else + { + mPreeditOverwrittenWString.clear(); + } + + segment_vec_t segments; + //pass empty segments to let "insertStringNoUndo" make new LLNormalTextSegment and insert it, if needed. + insertStringNoUndo(insert_preedit_at, mPreeditWString, &segments); + + mPreeditStandouts = preedit_standouts; + + setCursorPos(insert_preedit_at + caret_position); + + // Update of the preedit should be caused by some key strokes. + resetCursorBlink(); + + onKeyStroke(); +} + +bool LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +{ + if (control) + { + LLRect control_rect_screen; + localRectToScreen(mVisibleTextRect, &control_rect_screen); + LLUI::getInstance()->screenRectToGL(control_rect_screen, control); + } + + S32 preedit_left_position, preedit_right_position; + if (hasPreeditString()) + { + preedit_left_position = mPreeditPositions.front(); + preedit_right_position = mPreeditPositions.back(); + } + else + { + preedit_left_position = preedit_right_position = mCursorPos; + } + + const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos); + if (query < preedit_left_position || query > preedit_right_position) + { + return false; + } + + const S32 first_visible_line = getFirstVisibleLine(); + if (query < getLineStart(first_visible_line)) + { + return false; + } + + S32 current_line = first_visible_line; + S32 current_line_start, current_line_end; + for (;;) + { + current_line_start = getLineStart(current_line); + current_line_end = getLineStart(current_line + 1); + if (query >= current_line_start && query < current_line_end) + { + break; + } + if (current_line_start == current_line_end) + { + // We have reached on the last line. The query position must be here. + break; + } + current_line++; + } + + const LLWString textString(getWText()); + const llwchar * const text = textString.c_str(); + const S32 line_height = mFont->getLineHeight(); + + if (coord) + { + const S32 query_x = mVisibleTextRect.mLeft + mFont->getWidth(text, current_line_start, query - current_line_start); + const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; + S32 query_screen_x, query_screen_y; + localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); + LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); + } + + if (bounds) + { + S32 preedit_left = mVisibleTextRect.mLeft; + if (preedit_left_position > current_line_start) + { + preedit_left += mFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); + } + + S32 preedit_right = mVisibleTextRect.mLeft; + if (preedit_right_position < current_line_end) + { + preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); + } + else + { + preedit_right += mFont->getWidth(text, current_line_start, current_line_end - current_line_start); + } + + const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height; + const S32 preedit_bottom = preedit_top - line_height; + + const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom); + LLRect preedit_rect_screen; + localRectToScreen(preedit_rect_local, &preedit_rect_screen); + LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); + } + + return true; +} + +void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const +{ + if (hasSelection()) + { + *position = llmin(mSelectionStart, mSelectionEnd); + *length = llabs(mSelectionStart - mSelectionEnd); + } + else + { + *position = mCursorPos; + *length = 0; + } +} + +void LLTextEditor::getPreeditRange(S32 *position, S32 *length) const +{ + if (hasPreeditString()) + { + *position = mPreeditPositions.front(); + *length = mPreeditPositions.back() - mPreeditPositions.front(); + } + else + { + *position = mCursorPos; + *length = 0; + } +} + +void LLTextEditor::markAsPreedit(S32 position, S32 length) +{ + deselect(); + setCursorPos(position); + if (hasPreeditString()) + { + LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL; + } + mPreeditWString = LLWString( getWText(), position, length ); + if (length > 0) + { + mPreeditPositions.resize(2); + mPreeditPositions[0] = position; + mPreeditPositions[1] = position + length; + mPreeditStandouts.resize(1); + mPreeditStandouts[0] = false; + } + else + { + mPreeditPositions.clear(); + mPreeditStandouts.clear(); + } + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + mPreeditOverwrittenWString = mPreeditWString; + } + else + { + mPreeditOverwrittenWString.clear(); + } +} + +S32 LLTextEditor::getPreeditFontSize() const +{ + return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); +} + +bool LLTextEditor::isDirty() const +{ + if(mReadOnly) + { + return false; + } + + if( mPristineCmd ) + { + return ( mPristineCmd == mLastCmd ); + } + else + { + return ( NULL != mLastCmd ); + } +} + +void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback) +{ + mKeystrokeSignal.connect(callback); +} + +void LLTextEditor::onKeyStroke() +{ + mKeystrokeSignal(this); + + mSpellCheckStart = mSpellCheckEnd = -1; + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); +} + +//virtual +void LLTextEditor::clear() +{ + getViewModel()->setDisplay(LLWStringUtil::null); + clearSegments(); +} + +bool LLTextEditor::canLoadOrSaveToFile() +{ + return !mReadOnly; +} + +S32 LLTextEditor::spacesPerTab() +{ + return SPACES_PER_TAB; +} diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 2bffc813a5..be7f7cb256 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -1,351 +1,351 @@ -/** - * @file lltexteditor.h - * @brief LLTextEditor base class - * - * $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$ - */ - -// Text editor widget to let users enter a a multi-line ASCII document// - -#ifndef LL_LLTEXTEDITOR_H -#define LL_LLTEXTEDITOR_H - -#include "llrect.h" -#include "llframetimer.h" -#include "llstyle.h" -#include "lleditmenuhandler.h" -#include "llviewborder.h" // for params -#include "lltextbase.h" -#include "lltextvalidate.h" - -#include "llpreeditor.h" -#include "llcontrol.h" - -class LLFontGL; -class LLScrollbar; -class TextCmd; -class LLUICtrlFactory; -class LLScrollContainer; - -class LLTextEditor : - public LLTextBase, - protected LLPreeditor -{ -public: - struct Params : public LLInitParam::Block - { - Optional default_text; - Optional prevalidator; - - Optional embedded_items, - ignore_tab, - commit_on_focus_lost, - show_context_menu, - show_emoji_helper, - enable_tooltip_paste, - auto_indent; - - //colors - Optional default_color; - - Params(); - }; - - void initFromParams(const Params&); -protected: - LLTextEditor(const Params&); - friend class LLUICtrlFactory; -public: - // - // Constants - // - static const llwchar FIRST_EMBEDDED_CHAR = 0x100000; - static const llwchar LAST_EMBEDDED_CHAR = 0x10ffff; - static const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1; - - virtual ~LLTextEditor(); - - typedef boost::signals2::signal keystroke_signal_t; - - void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback); - - void setParseHighlights(bool parsing) {mParseHighlights=parsing;} - - static S32 spacesPerTab(); - - void insertEmoji(llwchar emoji); - void handleEmojiCommit(llwchar emoji); - - // mousehandler overrides - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleDoubleClick(S32 x, S32 y, MASK mask ); - virtual bool handleMiddleMouseDown(S32 x,S32 y,MASK mask); - - virtual bool handleKeyHere(KEY key, MASK mask ); - virtual bool handleUnicodeCharHere(llwchar uni_char); - - virtual void onMouseCaptureLost(); - - // view overrides - virtual void draw(); - virtual void onFocusReceived(); - virtual void onFocusLost(); - virtual void onCommit(); - virtual void setEnabled(bool enabled); - - // uictrl overrides - virtual void clear(); - virtual void setFocus( bool b ); - virtual bool isDirty() const; - - // LLEditMenuHandler interface - virtual void undo(); - virtual bool canUndo() const; - virtual void redo(); - virtual bool canRedo() const; - - virtual void cut(); - virtual bool canCut() const; - virtual void copy(); - virtual bool canCopy() const; - virtual void paste(); - virtual bool canPaste() const; - - virtual void updatePrimary(); - virtual void copyPrimary(); - virtual void pastePrimary(); - virtual bool canPastePrimary() const; - - virtual void doDelete(); - virtual bool canDoDelete() const; - virtual void selectAll(); - virtual bool canSelectAll() const; - - void selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos); - - virtual bool canLoadOrSaveToFile(); - - void selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap = true); - bool replaceText(const std::string& search_text, const std::string& replace_text, bool case_insensitive, bool wrap = true); - void replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive); - - // Undo/redo stack - void blockUndo(); - - // Text editing - virtual void makePristine(); - bool isPristine() const; - bool allowsEmbeddedItems() const { return mAllowEmbeddedItems; } - - // Autoreplace (formerly part of LLLineEditor) - typedef boost::function autoreplace_callback_t; - autoreplace_callback_t mAutoreplaceCallback; - void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; } - - /*virtual*/ void onSpellCheckPerformed(); - - // - // Text manipulation - // - - // inserts text at cursor - void insertText(const std::string &text); - void insertText(LLWString &text); - - void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo); - // Non-undoable - void setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params = LLStyle::Params()); - - - // Removes text from the end of document - // Does not change highlight or cursor position. - void removeTextFromEnd(S32 num_chars); - - bool tryToRevertToPristineState(); - - void setCursorAndScrollToEnd(); - - void getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap ); - - // Hacky methods to make it into a word-wrapping, potentially scrolling, - // read-only text box. - void setCommitOnFocusLost(bool b) { mCommitOnFocusLost = b; } - - // Hack to handle Notecards - virtual bool importBuffer(const char* buffer, S32 length ); - virtual bool exportBuffer(std::string& buffer ); - - const LLUUID& getSourceID() const { return mSourceID; } - - const LLTextSegmentPtr getPreviousSegment() const; - const LLTextSegmentPtr getLastSegment() const; - void getSelectedSegments(segment_vec_t& segments) const; - - void setShowContextMenu(bool show) { mShowContextMenu = show; } - bool getShowContextMenu() const { return mShowContextMenu; } - - void showEmojiHelper(); - void setShowEmojiHelper(bool show); - bool getShowEmojiHelper() const { return mShowEmojiHelper; } - - void setPassDelete(bool b) { mPassDelete = b; } - -protected: - void showContextMenu(S32 x, S32 y); - void drawPreeditMarker(); - - void assignEmbedded(const std::string &s); - - void removeCharOrTab(); - - void indentSelectedLines( S32 spaces ); - S32 indentLine( S32 pos, S32 spaces ); - void unindentLineBeforeCloseBrace(); - - virtual bool handleSpecialKey(const KEY key, const MASK mask); - bool handleNavigationKey(const KEY key, const MASK mask); - bool handleSelectionKey(const KEY key, const MASK mask); - bool handleControlKey(const KEY key, const MASK mask); - - bool selectionContainsLineBreaks(); - void deleteSelection(bool transient_operation); - - S32 prevWordPos(S32 cursorPos) const; - S32 nextWordPos(S32 cursorPos) const; - - void autoIndent(); - - void findEmbeddedItemSegments(S32 start, S32 end); - void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const; - - virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; } - - - // Here's the method that takes and applies text commands. - S32 execute(TextCmd* cmd); - - // Undoable operations - void addChar(llwchar c); // at mCursorPos - S32 addChar(S32 pos, llwchar wc); - void addLineBreakChar(bool group_together = false); - S32 overwriteChar(S32 pos, llwchar wc); - void removeChar(); - S32 removeChar(S32 pos); - S32 insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment); - S32 remove(S32 pos, S32 length, bool group_with_next_op); - - void tryToShowEmojiHelper(); - void focusLostHelper(); - void updateAllowingLanguageInput(); - bool hasPreeditString() const; - - // Overrides LLPreeditor - virtual void resetPreedit(); - virtual void updatePreedit(const LLWString &preedit_string, - const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position); - virtual void markAsPreedit(S32 position, S32 length); - virtual void getPreeditRange(S32 *position, S32 *length) const; - virtual void getSelectionRange(S32 *position, S32 *length) const; - virtual bool getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; - virtual S32 getPreeditFontSize() const; - virtual LLWString getPreeditString() const { return getWText(); } - // - // Protected data - // - // Probably deserves serious thought to hiding as many of these - // as possible behind protected accessor methods. - // - - // Use these to determine if a click on an embedded item is a drag or not. - S32 mMouseDownX; - S32 mMouseDownY; - - LLWString mPreeditWString; - LLWString mPreeditOverwrittenWString; - std::vector mPreeditPositions; - LLPreeditor::standouts_t mPreeditStandouts; - -protected: - LLUIColor mDefaultColor; - - bool mAutoIndent; - bool mParseOnTheFly; - - void updateLinkSegments(); - void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; } - class LLViewBorder* mBorder; - -private: - // - // Methods - // - void pasteHelper(bool is_primary); - void cleanStringForPaste(LLWString & clean_string); - void pasteTextWithLinebreaks(LLWString & clean_string); - - void onKeyStroke(); - - // Concrete TextCmd sub-classes used by the LLTextEditor base class - class TextCmdInsert; - class TextCmdAddChar; - class TextCmdOverwriteChar; - class TextCmdRemove; - - bool mBaseDocIsPristine; - TextCmd* mPristineCmd; - - TextCmd* mLastCmd; - - typedef std::deque undo_stack_t; - undo_stack_t mUndoStack; - - bool mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces - bool mCommitOnFocusLost; - bool mTakesFocus; - - bool mAllowEmbeddedItems; - bool mShowContextMenu; - bool mShowEmojiHelper; - bool mEnableTooltipPaste; - bool mPassDelete; - bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter - - LLUUID mSourceID; - - LLCoordGL mLastIMEPosition; // Last position of the IME editor - - keystroke_signal_t mKeystrokeSignal; - LLTextValidate::Validator mPrevalidator; - - LLHandle mContextMenuHandle; -}; // end class LLTextEditor - -// Build time optimization, generate once in .cpp file -#ifndef LLTEXTEDITOR_CPP -extern template class LLTextEditor* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -#endif // LL_TEXTEDITOR_H +/** + * @file lltexteditor.h + * @brief LLTextEditor base class + * + * $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$ + */ + +// Text editor widget to let users enter a a multi-line ASCII document// + +#ifndef LL_LLTEXTEDITOR_H +#define LL_LLTEXTEDITOR_H + +#include "llrect.h" +#include "llframetimer.h" +#include "llstyle.h" +#include "lleditmenuhandler.h" +#include "llviewborder.h" // for params +#include "lltextbase.h" +#include "lltextvalidate.h" + +#include "llpreeditor.h" +#include "llcontrol.h" + +class LLFontGL; +class LLScrollbar; +class TextCmd; +class LLUICtrlFactory; +class LLScrollContainer; + +class LLTextEditor : + public LLTextBase, + protected LLPreeditor +{ +public: + struct Params : public LLInitParam::Block + { + Optional default_text; + Optional prevalidator; + + Optional embedded_items, + ignore_tab, + commit_on_focus_lost, + show_context_menu, + show_emoji_helper, + enable_tooltip_paste, + auto_indent; + + //colors + Optional default_color; + + Params(); + }; + + void initFromParams(const Params&); +protected: + LLTextEditor(const Params&); + friend class LLUICtrlFactory; +public: + // + // Constants + // + static const llwchar FIRST_EMBEDDED_CHAR = 0x100000; + static const llwchar LAST_EMBEDDED_CHAR = 0x10ffff; + static const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1; + + virtual ~LLTextEditor(); + + typedef boost::signals2::signal keystroke_signal_t; + + void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback); + + void setParseHighlights(bool parsing) {mParseHighlights=parsing;} + + static S32 spacesPerTab(); + + void insertEmoji(llwchar emoji); + void handleEmojiCommit(llwchar emoji); + + // mousehandler overrides + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleDoubleClick(S32 x, S32 y, MASK mask ); + virtual bool handleMiddleMouseDown(S32 x,S32 y,MASK mask); + + virtual bool handleKeyHere(KEY key, MASK mask ); + virtual bool handleUnicodeCharHere(llwchar uni_char); + + virtual void onMouseCaptureLost(); + + // view overrides + virtual void draw(); + virtual void onFocusReceived(); + virtual void onFocusLost(); + virtual void onCommit(); + virtual void setEnabled(bool enabled); + + // uictrl overrides + virtual void clear(); + virtual void setFocus( bool b ); + virtual bool isDirty() const; + + // LLEditMenuHandler interface + virtual void undo(); + virtual bool canUndo() const; + virtual void redo(); + virtual bool canRedo() const; + + virtual void cut(); + virtual bool canCut() const; + virtual void copy(); + virtual bool canCopy() const; + virtual void paste(); + virtual bool canPaste() const; + + virtual void updatePrimary(); + virtual void copyPrimary(); + virtual void pastePrimary(); + virtual bool canPastePrimary() const; + + virtual void doDelete(); + virtual bool canDoDelete() const; + virtual void selectAll(); + virtual bool canSelectAll() const; + + void selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos); + + virtual bool canLoadOrSaveToFile(); + + void selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap = true); + bool replaceText(const std::string& search_text, const std::string& replace_text, bool case_insensitive, bool wrap = true); + void replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive); + + // Undo/redo stack + void blockUndo(); + + // Text editing + virtual void makePristine(); + bool isPristine() const; + bool allowsEmbeddedItems() const { return mAllowEmbeddedItems; } + + // Autoreplace (formerly part of LLLineEditor) + typedef boost::function autoreplace_callback_t; + autoreplace_callback_t mAutoreplaceCallback; + void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; } + + /*virtual*/ void onSpellCheckPerformed(); + + // + // Text manipulation + // + + // inserts text at cursor + void insertText(const std::string &text); + void insertText(LLWString &text); + + void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo); + // Non-undoable + void setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params = LLStyle::Params()); + + + // Removes text from the end of document + // Does not change highlight or cursor position. + void removeTextFromEnd(S32 num_chars); + + bool tryToRevertToPristineState(); + + void setCursorAndScrollToEnd(); + + void getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap ); + + // Hacky methods to make it into a word-wrapping, potentially scrolling, + // read-only text box. + void setCommitOnFocusLost(bool b) { mCommitOnFocusLost = b; } + + // Hack to handle Notecards + virtual bool importBuffer(const char* buffer, S32 length ); + virtual bool exportBuffer(std::string& buffer ); + + const LLUUID& getSourceID() const { return mSourceID; } + + const LLTextSegmentPtr getPreviousSegment() const; + const LLTextSegmentPtr getLastSegment() const; + void getSelectedSegments(segment_vec_t& segments) const; + + void setShowContextMenu(bool show) { mShowContextMenu = show; } + bool getShowContextMenu() const { return mShowContextMenu; } + + void showEmojiHelper(); + void setShowEmojiHelper(bool show); + bool getShowEmojiHelper() const { return mShowEmojiHelper; } + + void setPassDelete(bool b) { mPassDelete = b; } + +protected: + void showContextMenu(S32 x, S32 y); + void drawPreeditMarker(); + + void assignEmbedded(const std::string &s); + + void removeCharOrTab(); + + void indentSelectedLines( S32 spaces ); + S32 indentLine( S32 pos, S32 spaces ); + void unindentLineBeforeCloseBrace(); + + virtual bool handleSpecialKey(const KEY key, const MASK mask); + bool handleNavigationKey(const KEY key, const MASK mask); + bool handleSelectionKey(const KEY key, const MASK mask); + bool handleControlKey(const KEY key, const MASK mask); + + bool selectionContainsLineBreaks(); + void deleteSelection(bool transient_operation); + + S32 prevWordPos(S32 cursorPos) const; + S32 nextWordPos(S32 cursorPos) const; + + void autoIndent(); + + void findEmbeddedItemSegments(S32 start, S32 end); + void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const; + + virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; } + + + // Here's the method that takes and applies text commands. + S32 execute(TextCmd* cmd); + + // Undoable operations + void addChar(llwchar c); // at mCursorPos + S32 addChar(S32 pos, llwchar wc); + void addLineBreakChar(bool group_together = false); + S32 overwriteChar(S32 pos, llwchar wc); + void removeChar(); + S32 removeChar(S32 pos); + S32 insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment); + S32 remove(S32 pos, S32 length, bool group_with_next_op); + + void tryToShowEmojiHelper(); + void focusLostHelper(); + void updateAllowingLanguageInput(); + bool hasPreeditString() const; + + // Overrides LLPreeditor + virtual void resetPreedit(); + virtual void updatePreedit(const LLWString &preedit_string, + const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position); + virtual void markAsPreedit(S32 position, S32 length); + virtual void getPreeditRange(S32 *position, S32 *length) const; + virtual void getSelectionRange(S32 *position, S32 *length) const; + virtual bool getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; + virtual S32 getPreeditFontSize() const; + virtual LLWString getPreeditString() const { return getWText(); } + // + // Protected data + // + // Probably deserves serious thought to hiding as many of these + // as possible behind protected accessor methods. + // + + // Use these to determine if a click on an embedded item is a drag or not. + S32 mMouseDownX; + S32 mMouseDownY; + + LLWString mPreeditWString; + LLWString mPreeditOverwrittenWString; + std::vector mPreeditPositions; + LLPreeditor::standouts_t mPreeditStandouts; + +protected: + LLUIColor mDefaultColor; + + bool mAutoIndent; + bool mParseOnTheFly; + + void updateLinkSegments(); + void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; } + class LLViewBorder* mBorder; + +private: + // + // Methods + // + void pasteHelper(bool is_primary); + void cleanStringForPaste(LLWString & clean_string); + void pasteTextWithLinebreaks(LLWString & clean_string); + + void onKeyStroke(); + + // Concrete TextCmd sub-classes used by the LLTextEditor base class + class TextCmdInsert; + class TextCmdAddChar; + class TextCmdOverwriteChar; + class TextCmdRemove; + + bool mBaseDocIsPristine; + TextCmd* mPristineCmd; + + TextCmd* mLastCmd; + + typedef std::deque undo_stack_t; + undo_stack_t mUndoStack; + + bool mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces + bool mCommitOnFocusLost; + bool mTakesFocus; + + bool mAllowEmbeddedItems; + bool mShowContextMenu; + bool mShowEmojiHelper; + bool mEnableTooltipPaste; + bool mPassDelete; + bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter + + LLUUID mSourceID; + + LLCoordGL mLastIMEPosition; // Last position of the IME editor + + keystroke_signal_t mKeystrokeSignal; + LLTextValidate::Validator mPrevalidator; + + LLHandle mContextMenuHandle; +}; // end class LLTextEditor + +// Build time optimization, generate once in .cpp file +#ifndef LLTEXTEDITOR_CPP +extern template class LLTextEditor* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +#endif // LL_TEXTEDITOR_H diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp index be195e1146..8e2bfe6ac2 100644 --- a/indra/llui/lltextparser.cpp +++ b/indra/llui/lltextparser.cpp @@ -1,239 +1,239 @@ -/** - * @file lltextparser.cpp - * - * $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 "linden_common.h" - -#include "lltextparser.h" - -#include "llsd.h" -#include "llsdserialize.h" -#include "llerror.h" -#include "lluuid.h" -#include "llstring.h" -#include "message.h" -#include "llmath.h" -#include "v4color.h" -#include "lldir.h" - -// -// Member Functions -// - -LLTextParser::LLTextParser() -: mLoaded(false) -{} - - -S32 LLTextParser::findPattern(const std::string &text, LLSD highlight) -{ - if (!highlight.has("pattern")) return -1; - - std::string pattern=std::string(highlight["pattern"]); - std::string ltext=text; - - if (!(bool)highlight["case_sensitive"]) - { - ltext = utf8str_tolower(text); - pattern= utf8str_tolower(pattern); - } - - size_t found=std::string::npos; - - switch ((S32)highlight["condition"]) - { - case CONTAINS: - found = ltext.find(pattern); - break; - case MATCHES: - found = (! ltext.compare(pattern) ? 0 : std::string::npos); - break; - case STARTS_WITH: - found = (! ltext.find(pattern) ? 0 : std::string::npos); - break; - case ENDS_WITH: - S32 pos = ltext.rfind(pattern); - if (pos >= 0 && (ltext.length()-pattern.length()==pos)) found = pos; - break; - } - return found; -} - -LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index) -{ - loadKeywords(); - - //evil recursive string atomizer. - LLSD ret_llsd, start_llsd, middle_llsd, end_llsd; - - for (S32 i=index;i= 0 ) - { - S32 end = std::string(mHighlights[i]["pattern"]).length(); - S32 len = text.length(); - EHighlightPosition newpart; - if (start==0) - { - start_llsd[0]["text"] =text.substr(0,end); - start_llsd[0]["color"]=mHighlights[i]["color"]; - - if (end < len) - { - if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE; - end_llsd=parsePartialLineHighlights(text.substr( end ),color,newpart,i); - } - } - else - { - if (part==START || part==WHOLE) newpart=START; else newpart=MIDDLE; - - start_llsd=parsePartialLineHighlights(text.substr(0,start),color,newpart,i+1); - - if (end < len) - { - middle_llsd[0]["text"] =text.substr(start,end); - middle_llsd[0]["color"]=mHighlights[i]["color"]; - - if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE; - - end_llsd=parsePartialLineHighlights(text.substr( (start+end) ),color,newpart,i); - } - else - { - end_llsd[0]["text"] =text.substr(start,end); - end_llsd[0]["color"]=mHighlights[i]["color"]; - } - } - - S32 retcount=0; - - //FIXME These loops should be wrapped into a subroutine. - for (LLSD::array_iterator iter = start_llsd.beginArray(); - iter != start_llsd.endArray();++iter) - { - LLSD highlight = *iter; - ret_llsd[retcount++]=highlight; - } - - for (LLSD::array_iterator iter = middle_llsd.beginArray(); - iter != middle_llsd.endArray();++iter) - { - LLSD highlight = *iter; - ret_llsd[retcount++]=highlight; - } - - for (LLSD::array_iterator iter = end_llsd.beginArray(); - iter != end_llsd.endArray();++iter) - { - LLSD highlight = *iter; - ret_llsd[retcount++]=highlight; - } - - return ret_llsd; - } - } - } - } - - //No patterns found. Just send back what was passed in. - ret_llsd[0]["text"] =text; - LLSD color_sd = color.getValue(); - ret_llsd[0]["color"]=color_sd; - return ret_llsd; -} - -bool LLTextParser::parseFullLineHighlights(const std::string &text, LLColor4 *color) -{ - loadKeywords(); - - for (S32 i=0;i= 0 ) - { - LLSD color_llsd = mHighlights[i]["color"]; - color->setValue(color_llsd); - return true; - } - } - } - return false; //No matches found. -} - -std::string LLTextParser::getFileName() -{ - std::string path=gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, ""); - - if (!path.empty()) - { - path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "highlights.xml"); - } - return path; -} - -void LLTextParser::loadKeywords() -{ - if (mLoaded) - {// keywords already loaded - return; - } - std::string filename=getFileName(); - if (!filename.empty()) - { - llifstream file; - file.open(filename.c_str()); - if (file.is_open()) - { - LLSDSerialize::fromXML(mHighlights, file); - } - file.close(); - mLoaded = true; - } -} - -bool LLTextParser::saveToDisk(LLSD highlights) -{ - mHighlights=highlights; - std::string filename=getFileName(); - if (filename.empty()) - { - LL_WARNS() << "LLTextParser::saveToDisk() no valid user directory." << LL_ENDL; - return false; - } - llofstream file; - file.open(filename.c_str()); - LLSDSerialize::toPrettyXML(mHighlights, file); - file.close(); - return true; -} +/** + * @file lltextparser.cpp + * + * $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 "linden_common.h" + +#include "lltextparser.h" + +#include "llsd.h" +#include "llsdserialize.h" +#include "llerror.h" +#include "lluuid.h" +#include "llstring.h" +#include "message.h" +#include "llmath.h" +#include "v4color.h" +#include "lldir.h" + +// +// Member Functions +// + +LLTextParser::LLTextParser() +: mLoaded(false) +{} + + +S32 LLTextParser::findPattern(const std::string &text, LLSD highlight) +{ + if (!highlight.has("pattern")) return -1; + + std::string pattern=std::string(highlight["pattern"]); + std::string ltext=text; + + if (!(bool)highlight["case_sensitive"]) + { + ltext = utf8str_tolower(text); + pattern= utf8str_tolower(pattern); + } + + size_t found=std::string::npos; + + switch ((S32)highlight["condition"]) + { + case CONTAINS: + found = ltext.find(pattern); + break; + case MATCHES: + found = (! ltext.compare(pattern) ? 0 : std::string::npos); + break; + case STARTS_WITH: + found = (! ltext.find(pattern) ? 0 : std::string::npos); + break; + case ENDS_WITH: + S32 pos = ltext.rfind(pattern); + if (pos >= 0 && (ltext.length()-pattern.length()==pos)) found = pos; + break; + } + return found; +} + +LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index) +{ + loadKeywords(); + + //evil recursive string atomizer. + LLSD ret_llsd, start_llsd, middle_llsd, end_llsd; + + for (S32 i=index;i= 0 ) + { + S32 end = std::string(mHighlights[i]["pattern"]).length(); + S32 len = text.length(); + EHighlightPosition newpart; + if (start==0) + { + start_llsd[0]["text"] =text.substr(0,end); + start_llsd[0]["color"]=mHighlights[i]["color"]; + + if (end < len) + { + if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE; + end_llsd=parsePartialLineHighlights(text.substr( end ),color,newpart,i); + } + } + else + { + if (part==START || part==WHOLE) newpart=START; else newpart=MIDDLE; + + start_llsd=parsePartialLineHighlights(text.substr(0,start),color,newpart,i+1); + + if (end < len) + { + middle_llsd[0]["text"] =text.substr(start,end); + middle_llsd[0]["color"]=mHighlights[i]["color"]; + + if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE; + + end_llsd=parsePartialLineHighlights(text.substr( (start+end) ),color,newpart,i); + } + else + { + end_llsd[0]["text"] =text.substr(start,end); + end_llsd[0]["color"]=mHighlights[i]["color"]; + } + } + + S32 retcount=0; + + //FIXME These loops should be wrapped into a subroutine. + for (LLSD::array_iterator iter = start_llsd.beginArray(); + iter != start_llsd.endArray();++iter) + { + LLSD highlight = *iter; + ret_llsd[retcount++]=highlight; + } + + for (LLSD::array_iterator iter = middle_llsd.beginArray(); + iter != middle_llsd.endArray();++iter) + { + LLSD highlight = *iter; + ret_llsd[retcount++]=highlight; + } + + for (LLSD::array_iterator iter = end_llsd.beginArray(); + iter != end_llsd.endArray();++iter) + { + LLSD highlight = *iter; + ret_llsd[retcount++]=highlight; + } + + return ret_llsd; + } + } + } + } + + //No patterns found. Just send back what was passed in. + ret_llsd[0]["text"] =text; + LLSD color_sd = color.getValue(); + ret_llsd[0]["color"]=color_sd; + return ret_llsd; +} + +bool LLTextParser::parseFullLineHighlights(const std::string &text, LLColor4 *color) +{ + loadKeywords(); + + for (S32 i=0;i= 0 ) + { + LLSD color_llsd = mHighlights[i]["color"]; + color->setValue(color_llsd); + return true; + } + } + } + return false; //No matches found. +} + +std::string LLTextParser::getFileName() +{ + std::string path=gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, ""); + + if (!path.empty()) + { + path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "highlights.xml"); + } + return path; +} + +void LLTextParser::loadKeywords() +{ + if (mLoaded) + {// keywords already loaded + return; + } + std::string filename=getFileName(); + if (!filename.empty()) + { + llifstream file; + file.open(filename.c_str()); + if (file.is_open()) + { + LLSDSerialize::fromXML(mHighlights, file); + } + file.close(); + mLoaded = true; + } +} + +bool LLTextParser::saveToDisk(LLSD highlights) +{ + mHighlights=highlights; + std::string filename=getFileName(); + if (filename.empty()) + { + LL_WARNS() << "LLTextParser::saveToDisk() no valid user directory." << LL_ENDL; + return false; + } + llofstream file; + file.open(filename.c_str()); + LLSDSerialize::toPrettyXML(mHighlights, file); + file.close(); + return true; +} diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp index 5c298c9080..f1bf60d262 100644 --- a/indra/llui/lltimectrl.cpp +++ b/indra/llui/lltimectrl.cpp @@ -1,454 +1,454 @@ -/** - * @file lltimectrl.cpp - * @brief LLTimeCtrl base class - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, 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 "lltimectrl.h" - -#include "llui.h" -#include "lluiconstants.h" - -#include "llbutton.h" -#include "llfontgl.h" -#include "lllineeditor.h" -#include "llkeyboard.h" -#include "llstring.h" -#include "lltextbox.h" -#include "lluictrlfactory.h" - -static LLDefaultChildRegistry::Register time_r("time"); - -const U32 AMPM_LEN = 3; -const U32 MINUTES_MIN = 0; -const U32 MINUTES_MAX = 59; -const U32 HOURS_MIN = 1; -const U32 HOURS_MAX = 12; -const U32 MINUTES_PER_HOUR = 60; -const U32 MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR; - -class LLTimeValidatorImpl : public LLTextValidate::ValidatorImpl -{ -public: - // virtual - bool validate(const std::string& str) override - { - std::string hours = LLTimeCtrl::getHoursString(str); - if (!LLTimeCtrl::isHoursStringValid(hours)) - return setError("ValidatorInvalidHours", LLSD().with("STR", hours)); - - std::string minutes = LLTimeCtrl::getMinutesString(str); - if (!LLTimeCtrl::isMinutesStringValid(minutes)) - return setError("ValidatorInvalidMinutes", LLSD().with("STR", minutes)); - - std::string ampm = LLTimeCtrl::getAMPMString(str); - if (!LLTimeCtrl::isPMAMStringValid(ampm)) - return setError("ValidatorInvalidAMPM", LLSD().with("STR", ampm)); - - return resetError(); - } - - // virtual - bool validate(const LLWString& wstr) override - { - std::string str = wstring_to_utf8str(wstr); - - return validate(str); - } -} validateTimeImpl; -LLTextValidate::Validator validateTime(validateTimeImpl); - -LLTimeCtrl::Params::Params() -: label_width("label_width"), - snap_to("snap_to"), - allow_text_entry("allow_text_entry", true), - text_enabled_color("text_enabled_color"), - text_disabled_color("text_disabled_color"), - up_button("up_button"), - down_button("down_button") -{} - -LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p) -: LLUICtrl(p), - mLabelBox(NULL), - mTextEnabledColor(p.text_enabled_color()), - mTextDisabledColor(p.text_disabled_color()), - mTime(0), - mSnapToMin(5) -{ - static LLUICachedControl spinctrl_spacing ("UISpinctrlSpacing", 0); - static LLUICachedControl spinctrl_btn_width ("UISpinctrlBtnWidth", 0); - static LLUICachedControl spinctrl_btn_height ("UISpinctrlBtnHeight", 0); - S32 centered_top = getRect().getHeight(); - S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height; - S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40)); - S32 editor_left = label_width + spinctrl_spacing; - - //================= Label =================// - if( !p.label().empty() ) - { - LLRect label_rect( 0, centered_top, label_width, centered_bottom ); - LLTextBox::Params params; - params.name("TimeCtrl Label"); - params.rect(label_rect); - params.initial_value(p.label()); - if (p.font.isProvided()) - { - params.font(p.font); - } - mLabelBox = LLUICtrlFactory::create (params); - addChild(mLabelBox); - - editor_left = label_rect.mRight + spinctrl_spacing; - } - - S32 editor_right = getRect().getWidth() - spinctrl_btn_width - spinctrl_spacing; - - //================= Editor ================// - LLRect editor_rect( editor_left, centered_top, editor_right, centered_bottom ); - LLLineEditor::Params params; - params.name("SpinCtrl Editor"); - params.rect(editor_rect); - if (p.font.isProvided()) - { - params.font(p.font); - } - - params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); - params.max_length.chars(8); - params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1)); - mEditor = LLUICtrlFactory::create (params); - mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); - mEditor->setPrevalidate(validateTime); - mEditor->setText(LLStringExplicit("12:00 AM")); - addChild(mEditor); - - //================= Spin Buttons ==========// - LLButton::Params up_button_params(p.up_button); - up_button_params.rect = LLRect(editor_right + 1, getRect().getHeight(), editor_right + spinctrl_btn_width, getRect().getHeight() - spinctrl_btn_height); - - up_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this)); - up_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this)); - mUpBtn = LLUICtrlFactory::create(up_button_params); - addChild(mUpBtn); - - LLButton::Params down_button_params(p.down_button); - down_button_params.rect = LLRect(editor_right + 1, getRect().getHeight() - spinctrl_btn_height, editor_right + spinctrl_btn_width, getRect().getHeight() - 2 * spinctrl_btn_height); - down_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this)); - down_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this)); - mDownBtn = LLUICtrlFactory::create(down_button_params); - addChild(mDownBtn); - - setUseBoundingRect( true ); -} - -F32 LLTimeCtrl::getTime24() const -{ - // 0.0 - 23.99; - return mTime / 60.0f; -} - -U32 LLTimeCtrl::getHours24() const -{ - return (U32) getTime24(); -} - -U32 LLTimeCtrl::getMinutes() const -{ - return mTime % MINUTES_PER_HOUR; -} - -void LLTimeCtrl::setTime24(F32 time) -{ - time = llclamp(time, 0.0f, 23.99f); // fix out of range values - mTime = ll_round(time * MINUTES_PER_HOUR); // fixes values like 4.99999 - - updateText(); -} - -bool LLTimeCtrl::handleKeyHere(KEY key, MASK mask) -{ - if (mEditor->hasFocus()) - { - if(key == KEY_UP) - { - onUpBtn(); - return true; - } - if(key == KEY_DOWN) - { - onDownBtn(); - return true; - } - if (key == KEY_RETURN) - { - onCommit(); - return true; - } - } - return false; -} - -void LLTimeCtrl::onUpBtn() -{ - switch(getEditingPart()) - { - case HOURS: - increaseHours(); - break; - case MINUTES: - increaseMinutes(); - break; - case DAYPART: - switchDayPeriod(); - break; - default: - break; - } - - updateText(); - onCommit(); -} - -void LLTimeCtrl::onDownBtn() -{ - switch(getEditingPart()) - { - case HOURS: - decreaseHours(); - break; - case MINUTES: - decreaseMinutes(); - break; - case DAYPART: - switchDayPeriod(); - break; - default: - break; - } - - updateText(); - onCommit(); -} - -void LLTimeCtrl::onFocusLost() -{ - updateText(); - onCommit(); - LLUICtrl::onFocusLost(); -} - -void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor) -{ - std::string time_str = line_editor->getText(); - U32 h12 = parseHours(getHoursString(time_str)); - U32 m = parseMinutes(getMinutesString(time_str)); - bool pm = parseAMPM(getAMPMString(time_str)); - - if (h12 == 12) - { - h12 = 0; - } - - U32 h24 = pm ? h12 + 12 : h12; - - mTime = h24 * MINUTES_PER_HOUR + m; -} - -void LLTimeCtrl::increaseMinutes() -{ - mTime = (mTime + mSnapToMin) % MINUTES_PER_DAY - (mTime % mSnapToMin); -} - -void LLTimeCtrl::increaseHours() -{ - mTime = (mTime + MINUTES_PER_HOUR) % MINUTES_PER_DAY; -} - -void LLTimeCtrl::decreaseMinutes() -{ - if (mTime < mSnapToMin) - { - mTime = MINUTES_PER_DAY - mTime; - } - - mTime -= (mTime % mSnapToMin) ? mTime % mSnapToMin : mSnapToMin; -} - -void LLTimeCtrl::decreaseHours() -{ - if (mTime < MINUTES_PER_HOUR) - { - mTime = 23 * MINUTES_PER_HOUR + mTime; - } - else - { - mTime -= MINUTES_PER_HOUR; - } -} - -bool LLTimeCtrl::isPM() const -{ - return mTime >= (MINUTES_PER_DAY / 2); -} - -void LLTimeCtrl::switchDayPeriod() -{ - if (isPM()) - { - mTime -= MINUTES_PER_DAY / 2; - } - else - { - mTime += MINUTES_PER_DAY / 2; - } -} - -void LLTimeCtrl::updateText() -{ - U32 h24 = getHours24(); - U32 m = getMinutes(); - U32 h12 = h24 > 12 ? h24 - 12 : h24; - - if (h12 == 0) - h12 = 12; - - mEditor->setText(llformat("%d:%02d %s", h12, m, isPM() ? "PM":"AM")); -} - -LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart() -{ - S32 cur_pos = mEditor->getCursor(); - std::string time_str = mEditor->getText(); - - S32 colon_index = time_str.find_first_of(':'); - - if (cur_pos <= colon_index) - { - return HOURS; - } - else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN)) - { - return MINUTES; - } - else if (cur_pos > (S32)(time_str.length() - AMPM_LEN)) - { - return DAYPART; - } - - return NONE; -} - -// static -std::string LLTimeCtrl::getHoursString(const std::string& str) -{ - size_t colon_index = str.find_first_of(':'); - std::string hours_str = str.substr(0, colon_index); - - return hours_str; -} - -// static -std::string LLTimeCtrl::getMinutesString(const std::string& str) -{ - size_t colon_index = str.find_first_of(':'); - ++colon_index; - - int minutes_len = str.length() - colon_index - AMPM_LEN; - std::string minutes_str = str.substr(colon_index, minutes_len); - - return minutes_str; -} - -// static -std::string LLTimeCtrl::getAMPMString(const std::string& str) -{ - return str.substr(str.size() - 2, 2); // returns last two characters -} - -// static -bool LLTimeCtrl::isHoursStringValid(const std::string& str) -{ - U32 hours; - if ((!LLStringUtil::convertToU32(str, hours) || (hours <= HOURS_MAX)) && str.length() < 3) - return true; - - return false; -} - -// static -bool LLTimeCtrl::isMinutesStringValid(const std::string& str) -{ - U32 minutes; - if (!LLStringUtil::convertToU32(str, minutes) || ((minutes <= MINUTES_MAX) && str.length() < 3)) - return true; - - return false; -} - -// static -bool LLTimeCtrl::isPMAMStringValid(const std::string& str) -{ - S32 len = str.length(); - - bool valid = (str[--len] == 'M') && (str[--len] == 'P' || str[len] == 'A'); - - return valid; -} - -// static -U32 LLTimeCtrl::parseHours(const std::string& str) -{ - U32 hours; - if (LLStringUtil::convertToU32(str, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX)) - { - return hours; - } - else - { - return HOURS_MIN; - } -} - -// static -U32 LLTimeCtrl::parseMinutes(const std::string& str) -{ - U32 minutes; - // not sure of this fix - clang doesnt like compare minutes U32 to >= MINUTES_MIN (0) but MINUTES_MIN can change - if (LLStringUtil::convertToU32(str, minutes) && ((S32)minutes >= MINUTES_MIN) && ((S32)minutes <= MINUTES_MAX)) - { - return minutes; - } - else - { - return MINUTES_MIN; - } -} - -// static -bool LLTimeCtrl::parseAMPM(const std::string& str) -{ - return str == "PM"; -} +/** + * @file lltimectrl.cpp + * @brief LLTimeCtrl base class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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 "lltimectrl.h" + +#include "llui.h" +#include "lluiconstants.h" + +#include "llbutton.h" +#include "llfontgl.h" +#include "lllineeditor.h" +#include "llkeyboard.h" +#include "llstring.h" +#include "lltextbox.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register time_r("time"); + +const U32 AMPM_LEN = 3; +const U32 MINUTES_MIN = 0; +const U32 MINUTES_MAX = 59; +const U32 HOURS_MIN = 1; +const U32 HOURS_MAX = 12; +const U32 MINUTES_PER_HOUR = 60; +const U32 MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR; + +class LLTimeValidatorImpl : public LLTextValidate::ValidatorImpl +{ +public: + // virtual + bool validate(const std::string& str) override + { + std::string hours = LLTimeCtrl::getHoursString(str); + if (!LLTimeCtrl::isHoursStringValid(hours)) + return setError("ValidatorInvalidHours", LLSD().with("STR", hours)); + + std::string minutes = LLTimeCtrl::getMinutesString(str); + if (!LLTimeCtrl::isMinutesStringValid(minutes)) + return setError("ValidatorInvalidMinutes", LLSD().with("STR", minutes)); + + std::string ampm = LLTimeCtrl::getAMPMString(str); + if (!LLTimeCtrl::isPMAMStringValid(ampm)) + return setError("ValidatorInvalidAMPM", LLSD().with("STR", ampm)); + + return resetError(); + } + + // virtual + bool validate(const LLWString& wstr) override + { + std::string str = wstring_to_utf8str(wstr); + + return validate(str); + } +} validateTimeImpl; +LLTextValidate::Validator validateTime(validateTimeImpl); + +LLTimeCtrl::Params::Params() +: label_width("label_width"), + snap_to("snap_to"), + allow_text_entry("allow_text_entry", true), + text_enabled_color("text_enabled_color"), + text_disabled_color("text_disabled_color"), + up_button("up_button"), + down_button("down_button") +{} + +LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p) +: LLUICtrl(p), + mLabelBox(NULL), + mTextEnabledColor(p.text_enabled_color()), + mTextDisabledColor(p.text_disabled_color()), + mTime(0), + mSnapToMin(5) +{ + static LLUICachedControl spinctrl_spacing ("UISpinctrlSpacing", 0); + static LLUICachedControl spinctrl_btn_width ("UISpinctrlBtnWidth", 0); + static LLUICachedControl spinctrl_btn_height ("UISpinctrlBtnHeight", 0); + S32 centered_top = getRect().getHeight(); + S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height; + S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40)); + S32 editor_left = label_width + spinctrl_spacing; + + //================= Label =================// + if( !p.label().empty() ) + { + LLRect label_rect( 0, centered_top, label_width, centered_bottom ); + LLTextBox::Params params; + params.name("TimeCtrl Label"); + params.rect(label_rect); + params.initial_value(p.label()); + if (p.font.isProvided()) + { + params.font(p.font); + } + mLabelBox = LLUICtrlFactory::create (params); + addChild(mLabelBox); + + editor_left = label_rect.mRight + spinctrl_spacing; + } + + S32 editor_right = getRect().getWidth() - spinctrl_btn_width - spinctrl_spacing; + + //================= Editor ================// + LLRect editor_rect( editor_left, centered_top, editor_right, centered_bottom ); + LLLineEditor::Params params; + params.name("SpinCtrl Editor"); + params.rect(editor_rect); + if (p.font.isProvided()) + { + params.font(p.font); + } + + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + params.max_length.chars(8); + params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1)); + mEditor = LLUICtrlFactory::create (params); + mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); + mEditor->setPrevalidate(validateTime); + mEditor->setText(LLStringExplicit("12:00 AM")); + addChild(mEditor); + + //================= Spin Buttons ==========// + LLButton::Params up_button_params(p.up_button); + up_button_params.rect = LLRect(editor_right + 1, getRect().getHeight(), editor_right + spinctrl_btn_width, getRect().getHeight() - spinctrl_btn_height); + + up_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this)); + up_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this)); + mUpBtn = LLUICtrlFactory::create(up_button_params); + addChild(mUpBtn); + + LLButton::Params down_button_params(p.down_button); + down_button_params.rect = LLRect(editor_right + 1, getRect().getHeight() - spinctrl_btn_height, editor_right + spinctrl_btn_width, getRect().getHeight() - 2 * spinctrl_btn_height); + down_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this)); + down_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this)); + mDownBtn = LLUICtrlFactory::create(down_button_params); + addChild(mDownBtn); + + setUseBoundingRect( true ); +} + +F32 LLTimeCtrl::getTime24() const +{ + // 0.0 - 23.99; + return mTime / 60.0f; +} + +U32 LLTimeCtrl::getHours24() const +{ + return (U32) getTime24(); +} + +U32 LLTimeCtrl::getMinutes() const +{ + return mTime % MINUTES_PER_HOUR; +} + +void LLTimeCtrl::setTime24(F32 time) +{ + time = llclamp(time, 0.0f, 23.99f); // fix out of range values + mTime = ll_round(time * MINUTES_PER_HOUR); // fixes values like 4.99999 + + updateText(); +} + +bool LLTimeCtrl::handleKeyHere(KEY key, MASK mask) +{ + if (mEditor->hasFocus()) + { + if(key == KEY_UP) + { + onUpBtn(); + return true; + } + if(key == KEY_DOWN) + { + onDownBtn(); + return true; + } + if (key == KEY_RETURN) + { + onCommit(); + return true; + } + } + return false; +} + +void LLTimeCtrl::onUpBtn() +{ + switch(getEditingPart()) + { + case HOURS: + increaseHours(); + break; + case MINUTES: + increaseMinutes(); + break; + case DAYPART: + switchDayPeriod(); + break; + default: + break; + } + + updateText(); + onCommit(); +} + +void LLTimeCtrl::onDownBtn() +{ + switch(getEditingPart()) + { + case HOURS: + decreaseHours(); + break; + case MINUTES: + decreaseMinutes(); + break; + case DAYPART: + switchDayPeriod(); + break; + default: + break; + } + + updateText(); + onCommit(); +} + +void LLTimeCtrl::onFocusLost() +{ + updateText(); + onCommit(); + LLUICtrl::onFocusLost(); +} + +void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor) +{ + std::string time_str = line_editor->getText(); + U32 h12 = parseHours(getHoursString(time_str)); + U32 m = parseMinutes(getMinutesString(time_str)); + bool pm = parseAMPM(getAMPMString(time_str)); + + if (h12 == 12) + { + h12 = 0; + } + + U32 h24 = pm ? h12 + 12 : h12; + + mTime = h24 * MINUTES_PER_HOUR + m; +} + +void LLTimeCtrl::increaseMinutes() +{ + mTime = (mTime + mSnapToMin) % MINUTES_PER_DAY - (mTime % mSnapToMin); +} + +void LLTimeCtrl::increaseHours() +{ + mTime = (mTime + MINUTES_PER_HOUR) % MINUTES_PER_DAY; +} + +void LLTimeCtrl::decreaseMinutes() +{ + if (mTime < mSnapToMin) + { + mTime = MINUTES_PER_DAY - mTime; + } + + mTime -= (mTime % mSnapToMin) ? mTime % mSnapToMin : mSnapToMin; +} + +void LLTimeCtrl::decreaseHours() +{ + if (mTime < MINUTES_PER_HOUR) + { + mTime = 23 * MINUTES_PER_HOUR + mTime; + } + else + { + mTime -= MINUTES_PER_HOUR; + } +} + +bool LLTimeCtrl::isPM() const +{ + return mTime >= (MINUTES_PER_DAY / 2); +} + +void LLTimeCtrl::switchDayPeriod() +{ + if (isPM()) + { + mTime -= MINUTES_PER_DAY / 2; + } + else + { + mTime += MINUTES_PER_DAY / 2; + } +} + +void LLTimeCtrl::updateText() +{ + U32 h24 = getHours24(); + U32 m = getMinutes(); + U32 h12 = h24 > 12 ? h24 - 12 : h24; + + if (h12 == 0) + h12 = 12; + + mEditor->setText(llformat("%d:%02d %s", h12, m, isPM() ? "PM":"AM")); +} + +LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart() +{ + S32 cur_pos = mEditor->getCursor(); + std::string time_str = mEditor->getText(); + + S32 colon_index = time_str.find_first_of(':'); + + if (cur_pos <= colon_index) + { + return HOURS; + } + else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN)) + { + return MINUTES; + } + else if (cur_pos > (S32)(time_str.length() - AMPM_LEN)) + { + return DAYPART; + } + + return NONE; +} + +// static +std::string LLTimeCtrl::getHoursString(const std::string& str) +{ + size_t colon_index = str.find_first_of(':'); + std::string hours_str = str.substr(0, colon_index); + + return hours_str; +} + +// static +std::string LLTimeCtrl::getMinutesString(const std::string& str) +{ + size_t colon_index = str.find_first_of(':'); + ++colon_index; + + int minutes_len = str.length() - colon_index - AMPM_LEN; + std::string minutes_str = str.substr(colon_index, minutes_len); + + return minutes_str; +} + +// static +std::string LLTimeCtrl::getAMPMString(const std::string& str) +{ + return str.substr(str.size() - 2, 2); // returns last two characters +} + +// static +bool LLTimeCtrl::isHoursStringValid(const std::string& str) +{ + U32 hours; + if ((!LLStringUtil::convertToU32(str, hours) || (hours <= HOURS_MAX)) && str.length() < 3) + return true; + + return false; +} + +// static +bool LLTimeCtrl::isMinutesStringValid(const std::string& str) +{ + U32 minutes; + if (!LLStringUtil::convertToU32(str, minutes) || ((minutes <= MINUTES_MAX) && str.length() < 3)) + return true; + + return false; +} + +// static +bool LLTimeCtrl::isPMAMStringValid(const std::string& str) +{ + S32 len = str.length(); + + bool valid = (str[--len] == 'M') && (str[--len] == 'P' || str[len] == 'A'); + + return valid; +} + +// static +U32 LLTimeCtrl::parseHours(const std::string& str) +{ + U32 hours; + if (LLStringUtil::convertToU32(str, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX)) + { + return hours; + } + else + { + return HOURS_MIN; + } +} + +// static +U32 LLTimeCtrl::parseMinutes(const std::string& str) +{ + U32 minutes; + // not sure of this fix - clang doesnt like compare minutes U32 to >= MINUTES_MIN (0) but MINUTES_MIN can change + if (LLStringUtil::convertToU32(str, minutes) && ((S32)minutes >= MINUTES_MIN) && ((S32)minutes <= MINUTES_MAX)) + { + return minutes; + } + else + { + return MINUTES_MIN; + } +} + +// static +bool LLTimeCtrl::parseAMPM(const std::string& str) +{ + return str == "PM"; +} diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h index b5f2008f4b..6973c9a02b 100644 --- a/indra/llui/lltimectrl.h +++ b/indra/llui/lltimectrl.h @@ -1,129 +1,129 @@ -/** - * @file lltimectrl.h - * @brief Time control - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, 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 LLTIMECTRL_H_ -#define LLTIMECTRL_H_ - -#include "stdtypes.h" -#include "llbutton.h" -#include "v4color.h" -#include "llrect.h" - -class LLLineEditor; - -class LLTimeCtrl -: public LLUICtrl -{ - LOG_CLASS(LLTimeCtrl); -public: - struct Params : public LLInitParam::Block - { - Optional label_width; - Optional snap_to; - Optional allow_text_entry; - - Optional text_enabled_color; - Optional text_disabled_color; - - Optional up_button; - Optional down_button; - - Params(); - }; - - F32 getTime24() const; // 0.0 - 24.0 - U32 getHours24() const; // 0 - 23 - U32 getMinutes() const; // 0 - 59 - - void setTime24(F32 time); // 0.0 - 23.98(3) - - static std::string getHoursString(const std::string& str); - static std::string getMinutesString(const std::string& str); - static std::string getAMPMString(const std::string& str); - - static bool isHoursStringValid(const std::string& str); - static bool isMinutesStringValid(const std::string& str); - static bool isPMAMStringValid(const std::string& str); - - static U32 parseHours(const std::string& str); - static U32 parseMinutes(const std::string& str); - static bool parseAMPM(const std::string& str); - -protected: - LLTimeCtrl(const Params&); - friend class LLUICtrlFactory; - -private: - - enum EDayPeriod - { - AM, - PM - }; - - enum EEditingPart - { - HOURS, - MINUTES, - DAYPART, - NONE - }; - - virtual void onFocusLost(); - virtual bool handleKeyHere(KEY key, MASK mask); - - void onUpBtn(); - void onDownBtn(); - void onTextEntry(LLLineEditor* line_editor); - - void increaseMinutes(); - void increaseHours(); - - void decreaseMinutes(); - void decreaseHours(); - - bool isPM() const; - void switchDayPeriod(); - - void updateText(); - - EEditingPart getEditingPart(); - - class LLTextBox* mLabelBox; - - class LLLineEditor* mEditor; - LLUIColor mTextEnabledColor; - LLUIColor mTextDisabledColor; - - class LLButton* mUpBtn; - class LLButton* mDownBtn; - - U32 mTime; // minutes since midnight: 0 - 1439 - U32 mSnapToMin; // interval in minutes to snap to - - bool mAllowEdit; -}; -#endif /* LLTIMECTRL_H_ */ +/** + * @file lltimectrl.h + * @brief Time control + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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 LLTIMECTRL_H_ +#define LLTIMECTRL_H_ + +#include "stdtypes.h" +#include "llbutton.h" +#include "v4color.h" +#include "llrect.h" + +class LLLineEditor; + +class LLTimeCtrl +: public LLUICtrl +{ + LOG_CLASS(LLTimeCtrl); +public: + struct Params : public LLInitParam::Block + { + Optional label_width; + Optional snap_to; + Optional allow_text_entry; + + Optional text_enabled_color; + Optional text_disabled_color; + + Optional up_button; + Optional down_button; + + Params(); + }; + + F32 getTime24() const; // 0.0 - 24.0 + U32 getHours24() const; // 0 - 23 + U32 getMinutes() const; // 0 - 59 + + void setTime24(F32 time); // 0.0 - 23.98(3) + + static std::string getHoursString(const std::string& str); + static std::string getMinutesString(const std::string& str); + static std::string getAMPMString(const std::string& str); + + static bool isHoursStringValid(const std::string& str); + static bool isMinutesStringValid(const std::string& str); + static bool isPMAMStringValid(const std::string& str); + + static U32 parseHours(const std::string& str); + static U32 parseMinutes(const std::string& str); + static bool parseAMPM(const std::string& str); + +protected: + LLTimeCtrl(const Params&); + friend class LLUICtrlFactory; + +private: + + enum EDayPeriod + { + AM, + PM + }; + + enum EEditingPart + { + HOURS, + MINUTES, + DAYPART, + NONE + }; + + virtual void onFocusLost(); + virtual bool handleKeyHere(KEY key, MASK mask); + + void onUpBtn(); + void onDownBtn(); + void onTextEntry(LLLineEditor* line_editor); + + void increaseMinutes(); + void increaseHours(); + + void decreaseMinutes(); + void decreaseHours(); + + bool isPM() const; + void switchDayPeriod(); + + void updateText(); + + EEditingPart getEditingPart(); + + class LLTextBox* mLabelBox; + + class LLLineEditor* mEditor; + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; + + class LLButton* mUpBtn; + class LLButton* mDownBtn; + + U32 mTime; // minutes since midnight: 0 - 1439 + U32 mSnapToMin; // interval in minutes to snap to + + bool mAllowEdit; +}; +#endif /* LLTIMECTRL_H_ */ diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp index 09bd25b1a9..cfa520f693 100644 --- a/indra/llui/lltoggleablemenu.cpp +++ b/indra/llui/lltoggleablemenu.cpp @@ -1,108 +1,108 @@ -/** - * @file lltoggleablemenu.cpp - * @brief Menu toggled by a button press - * - * $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 "lltoggleablemenu.h" -#include "lluictrlfactory.h" - -static LLDefaultChildRegistry::Register r("toggleable_menu"); - -LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p) -: LLMenuGL(p), - mButtonRect(), - mVisibilityChangeSignal(NULL), - mClosedByButtonClick(false) -{ -} - -LLToggleableMenu::~LLToggleableMenu() -{ - delete mVisibilityChangeSignal; -} - -boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const commit_signal_t::slot_type& cb) -{ - if (!mVisibilityChangeSignal) mVisibilityChangeSignal = new commit_signal_t(); - return mVisibilityChangeSignal->connect(cb); -} - -// virtual -void LLToggleableMenu::onVisibilityChange (bool curVisibilityIn) -{ - S32 x,y; - LLUI::getInstance()->getMousePositionLocal(LLUI::getInstance()->getRootView(), &x, &y); - - // STORM-1879: also check MouseCapture to see if the button was really - // clicked (otherwise the VisibilityChange was triggered via keyboard shortcut) - if (!curVisibilityIn && mButtonRect.pointInRect(x, y) && gFocusMgr.getMouseCapture()) - { - mClosedByButtonClick = true; - } - - if (mVisibilityChangeSignal) - { - (*mVisibilityChangeSignal)(this, - LLSD().with("visibility", curVisibilityIn).with("closed_by_button_click", mClosedByButtonClick)); - } -} - -void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view) -{ - LLRect screen; - current_view->localRectToScreen(rect, &screen); - mButtonRect = screen; -} - -void LLToggleableMenu::setButtonRect(LLView* current_view) -{ - LLRect rect = current_view->getLocalRect(); - setButtonRect(rect, current_view); -} - -bool LLToggleableMenu::toggleVisibility() -{ - if (mClosedByButtonClick) - { - mClosedByButtonClick = false; - return false; - } - - if (getVisible()) - { - setVisible(false); - mClosedByButtonClick = false; - return false; - } - - return true; -} - -bool LLToggleableMenu::addChild(LLView* view, S32 tab_group) -{ - return addContextChild(view, tab_group); -} +/** + * @file lltoggleablemenu.cpp + * @brief Menu toggled by a button press + * + * $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 "lltoggleablemenu.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register r("toggleable_menu"); + +LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p) +: LLMenuGL(p), + mButtonRect(), + mVisibilityChangeSignal(NULL), + mClosedByButtonClick(false) +{ +} + +LLToggleableMenu::~LLToggleableMenu() +{ + delete mVisibilityChangeSignal; +} + +boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const commit_signal_t::slot_type& cb) +{ + if (!mVisibilityChangeSignal) mVisibilityChangeSignal = new commit_signal_t(); + return mVisibilityChangeSignal->connect(cb); +} + +// virtual +void LLToggleableMenu::onVisibilityChange (bool curVisibilityIn) +{ + S32 x,y; + LLUI::getInstance()->getMousePositionLocal(LLUI::getInstance()->getRootView(), &x, &y); + + // STORM-1879: also check MouseCapture to see if the button was really + // clicked (otherwise the VisibilityChange was triggered via keyboard shortcut) + if (!curVisibilityIn && mButtonRect.pointInRect(x, y) && gFocusMgr.getMouseCapture()) + { + mClosedByButtonClick = true; + } + + if (mVisibilityChangeSignal) + { + (*mVisibilityChangeSignal)(this, + LLSD().with("visibility", curVisibilityIn).with("closed_by_button_click", mClosedByButtonClick)); + } +} + +void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view) +{ + LLRect screen; + current_view->localRectToScreen(rect, &screen); + mButtonRect = screen; +} + +void LLToggleableMenu::setButtonRect(LLView* current_view) +{ + LLRect rect = current_view->getLocalRect(); + setButtonRect(rect, current_view); +} + +bool LLToggleableMenu::toggleVisibility() +{ + if (mClosedByButtonClick) + { + mClosedByButtonClick = false; + return false; + } + + if (getVisible()) + { + setVisible(false); + mClosedByButtonClick = false; + return false; + } + + return true; +} + +bool LLToggleableMenu::addChild(LLView* view, S32 tab_group) +{ + return addContextChild(view, tab_group); +} diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h index 665d81b4fa..7cbbd417a4 100644 --- a/indra/llui/lltoggleablemenu.h +++ b/indra/llui/lltoggleablemenu.h @@ -1,71 +1,71 @@ -/** - * @file lltoggleablemenu.h - * @brief Menu toggled by a button press - * - * $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_LLTOGGLEABLEMENU_H -#define LL_LLTOGGLEABLEMENU_H - -#include "llmenugl.h" - -class LLToggleableMenu : public LLMenuGL -{ -public: - //adding blank params to work around registration issue - //where LLToggleableMenu was owning the LLMenuGL param - //and menu.xml was never loaded - struct Params : public LLInitParam::Block - {}; -protected: - LLToggleableMenu(const Params&); - friend class LLUICtrlFactory; -public: - ~LLToggleableMenu(); - - boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb ); - - virtual void onVisibilityChange (bool curVisibilityIn); - - virtual bool addChild (LLView* view, S32 tab_group = 0); - - const LLRect& getButtonRect() const { return mButtonRect; } - - // Converts the given local button rect to a screen rect - void setButtonRect(const LLRect& rect, LLView* current_view); - void setButtonRect(LLView* current_view); - - // Returns "true" if menu was not closed by button click - // and is not still visible. If menu is visible toggles - // its visibility off. - bool toggleVisibility(); - - LLHandle getHandle() { return getDerivedHandle(); } - -protected: - bool mClosedByButtonClick; - LLRect mButtonRect; - commit_signal_t* mVisibilityChangeSignal; -}; - -#endif // LL_LLTOGGLEABLEMENU_H +/** + * @file lltoggleablemenu.h + * @brief Menu toggled by a button press + * + * $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_LLTOGGLEABLEMENU_H +#define LL_LLTOGGLEABLEMENU_H + +#include "llmenugl.h" + +class LLToggleableMenu : public LLMenuGL +{ +public: + //adding blank params to work around registration issue + //where LLToggleableMenu was owning the LLMenuGL param + //and menu.xml was never loaded + struct Params : public LLInitParam::Block + {}; +protected: + LLToggleableMenu(const Params&); + friend class LLUICtrlFactory; +public: + ~LLToggleableMenu(); + + boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb ); + + virtual void onVisibilityChange (bool curVisibilityIn); + + virtual bool addChild (LLView* view, S32 tab_group = 0); + + const LLRect& getButtonRect() const { return mButtonRect; } + + // Converts the given local button rect to a screen rect + void setButtonRect(const LLRect& rect, LLView* current_view); + void setButtonRect(LLView* current_view); + + // Returns "true" if menu was not closed by button click + // and is not still visible. If menu is visible toggles + // its visibility off. + bool toggleVisibility(); + + LLHandle getHandle() { return getDerivedHandle(); } + +protected: + bool mClosedByButtonClick; + LLRect mButtonRect; + commit_signal_t* mVisibilityChangeSignal; +}; + +#endif // LL_LLTOGGLEABLEMENU_H diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp index 9753881dfc..aa48ae048f 100644 --- a/indra/llui/lltoolbar.cpp +++ b/indra/llui/lltoolbar.cpp @@ -1,1266 +1,1266 @@ -/** - * @file lltoolbar.cpp - * @author Richard Nelson - * @brief User customizable toolbar class - * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, 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 "lltoolbar.h" - -#include "llcommandmanager.h" -#include "llmenugl.h" -#include "lltrans.h" -#include "llinventory.h" -#include "lliconctrl.h" - -// uncomment this and remove the one in llui.cpp when there is an external reference to this translation unit -// thanks, MSVC! -//static LLDefaultChildRegistry::Register r1("toolbar"); - -namespace LLToolBarEnums -{ - LLView::EOrientation getOrientation(SideType sideType) - { - LLView::EOrientation orientation = LLLayoutStack::HORIZONTAL; - - if ((sideType == SIDE_LEFT) || (sideType == SIDE_RIGHT)) - { - orientation = LLLayoutStack::VERTICAL; - } - - return orientation; - } -} - -using namespace LLToolBarEnums; - - -namespace LLInitParam -{ - void TypeValues::declareValues() - { - declare("icons_with_text", BTNTYPE_ICONS_WITH_TEXT); - declare("icons_only", BTNTYPE_ICONS_ONLY); - } - - void TypeValues::declareValues() - { - declare("bottom", SIDE_BOTTOM); - declare("left", SIDE_LEFT); - declare("right", SIDE_RIGHT); - declare("top", SIDE_TOP); - } -} - -LLToolBar::Params::Params() -: button_display_mode("button_display_mode"), - commands("command"), - side("side", SIDE_TOP), - button_icon("button_icon"), - button_icon_and_text("button_icon_and_text"), - read_only("read_only", false), - wrap("wrap", true), - pad_left("pad_left"), - pad_top("pad_top"), - pad_right("pad_right"), - pad_bottom("pad_bottom"), - pad_between("pad_between"), - min_girth("min_girth"), - button_panel("button_panel") -{} - -LLToolBar::LLToolBar(const LLToolBar::Params& p) -: LLUICtrl(p), - mReadOnly(p.read_only), - mButtonType(p.button_display_mode), - mSideType(p.side), - mWrap(p.wrap), - mNeedsLayout(false), - mModified(false), - mButtonPanel(NULL), - mCenteringStack(NULL), - mPadLeft(p.pad_left), - mPadRight(p.pad_right), - mPadTop(p.pad_top), - mPadBottom(p.pad_bottom), - mPadBetween(p.pad_between), - mMinGirth(p.min_girth), - mPopupMenuHandle(), - mRightMouseTargetButton(NULL), - mStartDragItemCallback(NULL), - mHandleDragItemCallback(NULL), - mHandleDropCallback(NULL), - mButtonAddSignal(NULL), - mButtonEnterSignal(NULL), - mButtonLeaveSignal(NULL), - mButtonRemoveSignal(NULL), - mDragAndDropTarget(false), - mCaretIcon(NULL), - mCenterPanel(NULL) -{ - mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_WITH_TEXT] = p.button_icon_and_text; - mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_ONLY] = p.button_icon; -} - -LLToolBar::~LLToolBar() -{ - auto menu = mPopupMenuHandle.get(); - if (menu) - { - menu->die(); - mPopupMenuHandle.markDead(); - } - delete mButtonAddSignal; - delete mButtonEnterSignal; - delete mButtonLeaveSignal; - delete mButtonRemoveSignal; -} - -void LLToolBar::createContextMenu() -{ - if (!mPopupMenuHandle.get()) - { - // Setup bindings specific to this instance for the context menu options - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit_reg; - commit_reg.add("Toolbars.EnableSetting", boost::bind(&LLToolBar::onSettingEnable, this, _2)); - commit_reg.add("Toolbars.RemoveSelectedCommand", boost::bind(&LLToolBar::onRemoveSelectedCommand, this)); - - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_reg; - enable_reg.add("Toolbars.CheckSetting", boost::bind(&LLToolBar::isSettingChecked, this, _2)); - - // Create the context menu - llassert(LLMenuGL::sMenuContainer != NULL); - LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile("menu_toolbars.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); - - if (menu) - { - menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); - mPopupMenuHandle = menu->getHandle(); - mRemoveButtonHandle = menu->getChild("Remove button")->getHandle(); - } - else - { - LL_WARNS() << "Unable to load toolbars context menu." << LL_ENDL; - } - } - - if (mRemoveButtonHandle.get()) - { - // Disable/Enable the "Remove button" menu item depending on whether or not a button was clicked - mRemoveButtonHandle.get()->setEnabled(mRightMouseTargetButton != NULL); - } -} - -void LLToolBar::initFromParams(const LLToolBar::Params& p) -{ - // Initialize the base object - LLUICtrl::initFromParams(p); - - LLView::EOrientation orientation = getOrientation(p.side); - - LLLayoutStack::Params centering_stack_p; - centering_stack_p.name = "centering_stack"; - centering_stack_p.rect = getLocalRect(); - centering_stack_p.follows.flags = FOLLOWS_ALL; - centering_stack_p.orientation = orientation; - centering_stack_p.mouse_opaque = false; - - mCenteringStack = LLUICtrlFactory::create(centering_stack_p); - addChild(mCenteringStack); - - LLLayoutPanel::Params border_panel_p; - border_panel_p.name = "border_panel"; - border_panel_p.rect = getLocalRect(); - border_panel_p.auto_resize = true; - border_panel_p.user_resize = false; - border_panel_p.mouse_opaque = false; - - mCenteringStack->addChild(LLUICtrlFactory::create(border_panel_p)); - - LLLayoutPanel::Params center_panel_p; - center_panel_p.name = "center_panel"; - center_panel_p.rect = getLocalRect(); - center_panel_p.auto_resize = false; - center_panel_p.user_resize = false; - center_panel_p.mouse_opaque = false; - mCenterPanel = LLUICtrlFactory::create(center_panel_p); - mCenteringStack->addChild(mCenterPanel); - - LLPanel::Params button_panel_p(p.button_panel); - button_panel_p.rect = mCenterPanel->getLocalRect(); - button_panel_p.follows.flags = FOLLOWS_BOTTOM|FOLLOWS_LEFT; - mButtonPanel = LLUICtrlFactory::create(button_panel_p); - mCenterPanel->setButtonPanel(mButtonPanel); - mCenterPanel->addChild(mButtonPanel); - - mCenteringStack->addChild(LLUICtrlFactory::create(border_panel_p)); - - for (const auto& id : p.commands) - { - addCommand(id); - } - - mNeedsLayout = true; -} - -bool LLToolBar::addCommand(const LLCommandId& commandId, int rank) -{ - LLCommand * command = LLCommandManager::instance().getCommand(commandId); - if (!command) return false; - - // Create the button and do the things that don't need ordering - LLToolBarButton* button = createButton(commandId); - mButtonPanel->addChild(button); - mButtonMap.insert(std::make_pair(commandId.uuid(), button)); - - // Insert the command and button in the right place in their respective lists - if ((rank >= mButtonCommands.size()) || (rank == RANK_NONE)) - { - // In that case, back load - mButtonCommands.push_back(command->id()); - mButtons.push_back(button); - } - else - { - // Insert in place: iterate to the right spot... - std::list::iterator it_button = mButtons.begin(); - command_id_list_t::iterator it_command = mButtonCommands.begin(); - while (rank > 0) - { - ++it_button; - ++it_command; - rank--; - } - // ...then insert - mButtonCommands.insert(it_command, command->id()); - mButtons.insert(it_button,button); - } - - mNeedsLayout = true; - - updateLayoutAsNeeded(); - - - if (mButtonAddSignal) - { - (*mButtonAddSignal)(button); - } - - return true; -} - -// Remove a command from the list -// Returns the rank of the command in the original list so that doing addCommand(id,rank) right after -// a removeCommand(id) would leave the list unchanged. -// Returns RANK_NONE if the command is not found in the list -int LLToolBar::removeCommand(const LLCommandId& commandId) -{ - if (!hasCommand(commandId)) return RANK_NONE; - - // First erase the map record - command_id_map::iterator it = mButtonMap.find(commandId.uuid()); - mButtonMap.erase(it); - - // Now iterate on the commands and buttons to identify the relevant records - int rank = 0; - std::list::iterator it_button = mButtons.begin(); - command_id_list_t::iterator it_command = mButtonCommands.begin(); - while (*it_command != commandId) - { - ++it_button; - ++it_command; - ++rank; - } - - if (mButtonRemoveSignal) - { - (*mButtonRemoveSignal)(*it_button); - } - - // Delete the button and erase the command and button records - delete (*it_button); - mButtonCommands.erase(it_command); - mButtons.erase(it_button); - - mNeedsLayout = true; - - return rank; -} - -void LLToolBar::clearCommandsList() -{ - // Clears the commands list - mButtonCommands.clear(); - // This will clear the buttons - createButtons(); -} - -bool LLToolBar::hasCommand(const LLCommandId& commandId) const -{ - if (commandId != LLCommandId::null) - { - command_id_map::const_iterator it = mButtonMap.find(commandId.uuid()); - return (it != mButtonMap.end()); - } - - return false; -} - -bool LLToolBar::enableCommand(const LLCommandId& commandId, bool enabled) -{ - LLButton * command_button = NULL; - - if (commandId != LLCommandId::null) - { - command_id_map::iterator it = mButtonMap.find(commandId.uuid()); - if (it != mButtonMap.end()) - { - command_button = it->second; - command_button->setEnabled(enabled); - } - } - - return (command_button != NULL); -} - -bool LLToolBar::stopCommandInProgress(const LLCommandId& commandId) -{ - // - // Note from Leslie: - // - // This implementation was largely put in place to handle EXP-1348 which is related to - // dragging and dropping the "speak" button. The "speak" button can be in one of two - // modes, i.e., either a toggle action or a push-to-talk action. Because of this it - // responds to mouse down and mouse up in different ways, based on which behavior the - // button is currently set to obey. This was the simplest way of getting the button - // to turn off the microphone for both behaviors without risking duplicate state. - // - - LLToolBarButton * command_button = NULL; - - if (commandId != LLCommandId::null) - { - LLCommand* command = LLCommandManager::instance().getCommand(commandId); - llassert(command); - - // If this command has an explicit function for execution stop - if (command->executeStopFunctionName().length() > 0) - { - command_id_map::iterator it = mButtonMap.find(commandId.uuid()); - if (it != mButtonMap.end()) - { - command_button = it->second; - llassert(command_button->mIsRunningSignal); - - // Check to see if it is running - if ((*command_button->mIsRunningSignal)(command_button, command->isRunningParameters())) - { - // Trigger an additional button commit, which calls mouse down, mouse up and commit - command_button->onCommit(); - } - } - } - } - - return (command_button != NULL); -} - -bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing/* = false */) -{ - LLButton * command_button = NULL; - - if (commandId != LLCommandId::null) - { - command_id_map::iterator it = mButtonMap.find(commandId.uuid()); - if (it != mButtonMap.end()) - { - command_button = it->second; - command_button->setFlashing((bool)(flash),(bool)(force_flashing)); - } - } - - return (command_button != NULL); -} - -bool LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - LLRect button_panel_rect; - mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this); - bool handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y); - - if (handle_it_here) - { - // Determine which button the mouse was over during the click in case the context menu action - // is intended to affect the button. - mRightMouseTargetButton = NULL; - for (LLToolBarButton* button : mButtons) - { - LLRect button_rect; - button->localRectToOtherView(button->getLocalRect(), &button_rect, this); - - if (button_rect.pointInRect(x, y)) - { - mRightMouseTargetButton = button; - break; - } - } - - createContextMenu(); - - LLContextMenu * menu = (LLContextMenu *) mPopupMenuHandle.get(); - - if (menu) - { - menu->show(x, y); - - LLMenuGL::showPopup(this, menu, x, y); - } - } - - return handle_it_here; -} - -bool LLToolBar::isSettingChecked(const LLSD& userdata) -{ - bool retval = false; - - const std::string setting_name = userdata.asString(); - - if (setting_name == "icons_with_text") - { - retval = (mButtonType == BTNTYPE_ICONS_WITH_TEXT); - } - else if (setting_name == "icons_only") - { - retval = (mButtonType == BTNTYPE_ICONS_ONLY); - } - - return retval; -} - -void LLToolBar::onSettingEnable(const LLSD& userdata) -{ - llassert(!mReadOnly); - - const std::string setting_name = userdata.asString(); - - if (setting_name == "icons_with_text") - { - setButtonType(BTNTYPE_ICONS_WITH_TEXT); - } - else if (setting_name == "icons_only") - { - setButtonType(BTNTYPE_ICONS_ONLY); - } -} - -void LLToolBar::onRemoveSelectedCommand() -{ - llassert(!mReadOnly); - - if (mRightMouseTargetButton) - { - removeCommand(mRightMouseTargetButton->getCommandId()); - - mRightMouseTargetButton = NULL; - } -} - -void LLToolBar::setButtonType(LLToolBarEnums::ButtonType button_type) -{ - bool regenerate_buttons = (mButtonType != button_type); - - mButtonType = button_type; - - if (regenerate_buttons) - { - createButtons(); - } -} - -void LLToolBar::resizeButtonsInRow(std::vector& buttons_in_row, S32 max_row_girth) -{ - // make buttons in current row all same girth - for (LLToolBarButton* button : buttons_in_row) - { - if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL) - { - button->reshape(button->mWidthRange.clamp(button->getRect().getWidth()), max_row_girth); - } - else // VERTICAL - { - button->reshape(max_row_girth, button->getRect().getHeight()); - } - } -} - -// Returns the position of the coordinates as a rank in the button list. -// The rank is the position a tool dropped in (x,y) would assume in the button list. -// The returned value is between 0 and mButtons.size(), 0 being the first element to the left -// (or top) and mButtons.size() the last one to the right (or bottom). -// Various drag data are stored in the toolbar object though are not exposed outside (and shouldn't). -int LLToolBar::getRankFromPosition(S32 x, S32 y) -{ - if (mButtons.empty()) - { - return RANK_NONE; - } - - int rank = 0; - - // Convert the toolbar coord into button panel coords - LLView::EOrientation orientation = getOrientation(mSideType); - S32 button_panel_x = 0; - S32 button_panel_y = 0; - localPointToOtherView(x, y, &button_panel_x, &button_panel_y, mButtonPanel); - S32 dx = x - button_panel_x; - S32 dy = y - button_panel_y; - - // Simply compare the passed coord with the buttons outbound box + padding - std::list::iterator it_button = mButtons.begin(); - std::list::iterator end_button = mButtons.end(); - LLRect button_rect; - while (it_button != end_button) - { - button_rect = (*it_button)->getRect(); - S32 point_x = button_rect.mRight + mPadRight; - S32 point_y = button_rect.mBottom - mPadBottom; - - if ((button_panel_x < point_x) && (button_panel_y > point_y)) - { - break; - } - rank++; - ++it_button; - } - - // Update the passed coordinates to the hit button relevant corner - // (different depending on toolbar orientation) - if (rank < mButtons.size()) - { - if (orientation == LLLayoutStack::HORIZONTAL) - { - // Horizontal - S32 mid_point = (button_rect.mRight + button_rect.mLeft) / 2; - if (button_panel_x < mid_point) - { - mDragx = button_rect.mLeft - mPadLeft; - mDragy = button_rect.mTop + mPadTop; - } - else - { - rank++; - mDragx = button_rect.mRight + mPadRight - 1; - mDragy = button_rect.mTop + mPadTop; - } - } - else - { - // Vertical - S32 mid_point = (button_rect.mTop + button_rect.mBottom) / 2; - if (button_panel_y > mid_point) - { - mDragx = button_rect.mLeft - mPadLeft; - mDragy = button_rect.mTop + mPadTop; - } - else - { - rank++; - mDragx = button_rect.mLeft - mPadLeft; - mDragy = button_rect.mBottom - mPadBottom + 1; - } - } - } - else - { - // We hit passed the end of the list so put the insertion point at the end - if (orientation == LLLayoutStack::HORIZONTAL) - { - mDragx = button_rect.mRight + mPadRight; - mDragy = button_rect.mTop + mPadTop; - } - else - { - mDragx = button_rect.mLeft - mPadLeft; - mDragy = button_rect.mBottom - mPadBottom; - } - } - - // Update the "girth" of the caret, i.e. the width or height (depending of orientation) - if (orientation == LLLayoutStack::HORIZONTAL) - { - mDragGirth = button_rect.getHeight() + mPadBottom + mPadTop; - } - else - { - mDragGirth = button_rect.getWidth() + mPadLeft + mPadRight; - } - - // The delta account for the coord model change (i.e. convert back to toolbar coord) - mDragx += dx; - mDragy += dy; - - return rank; -} - -int LLToolBar::getRankFromPosition(const LLCommandId& id) -{ - if (!hasCommand(id)) - { - return RANK_NONE; - } - int rank = 0; - std::list::iterator it_button = mButtons.begin(); - std::list::iterator end_button = mButtons.end(); - while (it_button != end_button) - { - if ((*it_button)->mId == id) - { - break; - } - rank++; - ++it_button; - } - return rank; -} - -void LLToolBar::updateLayoutAsNeeded() -{ - if (!mNeedsLayout) return; - - LLView::EOrientation orientation = getOrientation(mSideType); - - // our terminology for orientation-agnostic layout is such that - // length refers to a distance in the direction we stack the buttons - // and girth refers to a distance in the direction buttons wrap - S32 max_row_girth = 0; - S32 max_row_length = 0; - - S32 max_length; - S32 cur_start; - S32 cur_row ; - S32 row_pad_start; - S32 row_pad_end; - S32 girth_pad_end; - S32 row_running_length; - - if (orientation == LLLayoutStack::HORIZONTAL) - { - max_length = getRect().getWidth() - mPadLeft - mPadRight; - row_pad_start = mPadLeft; - row_pad_end = mPadRight; - cur_row = mPadTop; - girth_pad_end = mPadBottom; - } - else // VERTICAL - { - max_length = getRect().getHeight() - mPadTop - mPadBottom; - row_pad_start = mPadTop; - row_pad_end = mPadBottom; - cur_row = mPadLeft; - girth_pad_end = mPadRight; - } - - row_running_length = row_pad_start; - cur_start = row_pad_start; - - - LLRect panel_rect = mButtonPanel->getLocalRect(); - - std::vector buttons_in_row; - - for (LLToolBarButton* button : mButtons) - { - button->reshape(button->mWidthRange.getMin(), button->mDesiredHeight); - button->autoResize(); - - S32 button_clamped_width = button->mWidthRange.clamp(button->getRect().getWidth()); - S32 button_length = (orientation == LLLayoutStack::HORIZONTAL) - ? button_clamped_width - : button->getRect().getHeight(); - S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL) - ? button->getRect().getHeight() - : button_clamped_width; - - // wrap if needed - if (mWrap - && row_running_length + button_length > max_length // out of room... - && cur_start != row_pad_start) // ...and not first button in row - { - if (orientation == LLLayoutStack::VERTICAL) - { // row girth (width in this case) is clamped to allowable button widths - max_row_girth = button->mWidthRange.clamp(max_row_girth); - } - - // make buttons in current row all same girth - resizeButtonsInRow(buttons_in_row, max_row_girth); - buttons_in_row.clear(); - - max_row_length = llmax(max_row_length, row_running_length); - row_running_length = row_pad_start; - cur_start = row_pad_start; - cur_row += max_row_girth + mPadBetween; - max_row_girth = 0; - } - - LLRect button_rect; - if (orientation == LLLayoutStack::HORIZONTAL) - { - button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight()); - } - else // VERTICAL - { - button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight()); - } - button->setShape(button_rect); - - buttons_in_row.push_back(button); - - row_running_length += button_length + mPadBetween; - cur_start = row_running_length; - max_row_girth = llmax(button_girth, max_row_girth); - } - - // final resizing in "girth" direction - S32 total_girth = cur_row // current row position... - + max_row_girth // ...incremented by size of final row... - + girth_pad_end; // ...plus padding reserved on end - total_girth = llmax(total_girth,mMinGirth); - - max_row_length = llmax(max_row_length, row_running_length - mPadBetween + row_pad_end); - - resizeButtonsInRow(buttons_in_row, max_row_girth); - - // grow and optionally shift toolbar to accommodate buttons - if (orientation == LLLayoutStack::HORIZONTAL) - { - if (mSideType == SIDE_TOP) - { // shift down to maintain top edge - translate(0, getRect().getHeight() - total_girth); - } - - reshape(getRect().getWidth(), total_girth); - mButtonPanel->reshape(max_row_length, total_girth); - } - else // VERTICAL - { - if (mSideType == SIDE_RIGHT) - { // shift left to maintain right edge - translate(getRect().getWidth() - total_girth, 0); - } - - reshape(total_girth, getRect().getHeight()); - mButtonPanel->reshape(total_girth, max_row_length); - } - - // make parent fit button panel - mButtonPanel->getParent()->setShape(mButtonPanel->getLocalRect()); - - // re-center toolbar buttons - mCenteringStack->updateLayout(); - - if (!mButtons.empty()) - { - mButtonPanel->setVisible(true); - mButtonPanel->setMouseOpaque(true); - } - - // don't clear flag until after we've resized ourselves, to avoid laying out every frame - mNeedsLayout = false; -} - - -void LLToolBar::draw() -{ - if (mButtons.empty()) - { - mButtonPanel->setVisible(false); - mButtonPanel->setMouseOpaque(false); - } - else - { - mButtonPanel->setVisible(true); - mButtonPanel->setMouseOpaque(true); - } - - // Update enable/disable state and highlight state for editable toolbars - if (!mReadOnly) - { - for (toolbar_button_list::iterator btn_it = mButtons.begin(); btn_it != mButtons.end(); ++btn_it) - { - LLToolBarButton* btn = *btn_it; - LLCommand* command = LLCommandManager::instance().getCommand(btn->mId); - - if (command && btn->mIsEnabledSignal) - { - const bool button_command_enabled = (*btn->mIsEnabledSignal)(btn, command->isEnabledParameters()); - btn->setEnabled(button_command_enabled); - } - - if (command && btn->mIsRunningSignal) - { - const bool button_command_running = (*btn->mIsRunningSignal)(btn, command->isRunningParameters()); - btn->setToggleState(button_command_running); - } - } - } - - updateLayoutAsNeeded(); - // rect may have shifted during layout - LLUI::popMatrix(); - LLUI::pushMatrix(); - LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); - - // Position the caret - if (!mCaretIcon) - { - mCaretIcon = getChild("caret"); - } - - LLIconCtrl* caret = mCaretIcon; - caret->setVisible(false); - if (mDragAndDropTarget && !mButtonCommands.empty()) - { - LLRect caret_rect = caret->getRect(); - if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL) - { - caret->setRect(LLRect(mDragx-caret_rect.getWidth()/2+1, - mDragy, - mDragx+caret_rect.getWidth()/2+1, - mDragy-mDragGirth)); - } - else - { - caret->setRect(LLRect(mDragx, - mDragy+caret_rect.getHeight()/2, - mDragx+mDragGirth, - mDragy-caret_rect.getHeight()/2)); - } - caret->setVisible(true); - } - - LLUICtrl::draw(); - caret->setVisible(false); - mDragAndDropTarget = false; -} - -void LLToolBar::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLUICtrl::reshape(width, height, called_from_parent); - mNeedsLayout = true; -} - -void LLToolBar::createButtons() -{ - std::set set_flashing; - - for (LLToolBarButton* button : mButtons) - { - if (button->getFlashTimer() && button->getFlashTimer()->isFlashingInProgress()) - { - set_flashing.insert(button->getCommandId().uuid()); - } - - if (mButtonRemoveSignal) - { - (*mButtonRemoveSignal)(button); - } - - delete button; - } - mButtons.clear(); - mButtonMap.clear(); - mRightMouseTargetButton = NULL; - - for (const LLCommandId& command_id : mButtonCommands) - { - LLToolBarButton* button = createButton(command_id); - mButtons.push_back(button); - mButtonPanel->addChild(button); - mButtonMap.insert(std::make_pair(command_id.uuid(), button)); - - if (mButtonAddSignal) - { - (*mButtonAddSignal)(button); - } - - if (set_flashing.find(button->getCommandId().uuid()) != set_flashing.end()) - { - button->setFlashing(true); - } - } - mNeedsLayout = true; -} - -void LLToolBarButton::callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param ) -{ - LLCommand* command = LLCommandManager::instance().getCommand(mId); - - if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters())) - { - commit(ctrl, param); - } -} - -LLToolBarButton* LLToolBar::createButton(const LLCommandId& id) -{ - LLCommand* commandp = LLCommandManager::instance().getCommand(id); - if (!commandp) return NULL; - - LLToolBarButton::Params button_p; - button_p.name = commandp->name(); - button_p.label = LLTrans::getString(commandp->labelRef()); - button_p.tool_tip = LLTrans::getString(commandp->tooltipRef()); - button_p.image_overlay = LLUI::getUIImage(commandp->icon()); - button_p.button_flash_enable = commandp->isFlashingAllowed(); - button_p.overwriteFrom(mButtonParams[mButtonType]); - LLToolBarButton* button = LLUICtrlFactory::create(button_p); - - if (!mReadOnly) - { - enable_callback_t isEnabledCB; - - const std::string& isEnabledFunction = commandp->isEnabledFunctionName(); - if (isEnabledFunction.length() > 0) - { - LLUICtrl::EnableCallbackParam isEnabledParam; - isEnabledParam.function_name = isEnabledFunction; - isEnabledParam.parameter = commandp->isEnabledParameters(); - isEnabledCB = initEnableCallback(isEnabledParam); - - if (NULL == button->mIsEnabledSignal) - { - button->mIsEnabledSignal = new enable_signal_t(); - } - - button->mIsEnabledSignal->connect(isEnabledCB); - } - - LLUICtrl::CommitCallbackParam executeParam; - executeParam.function_name = commandp->executeFunctionName(); - executeParam.parameter = commandp->executeParameters(); - - // If we have a "stop" function then we map the command to mouse down / mouse up otherwise commit - const std::string& executeStopFunction = commandp->executeStopFunctionName(); - if (executeStopFunction.length() > 0) - { - LLUICtrl::CommitCallbackParam executeStopParam; - executeStopParam.function_name = executeStopFunction; - executeStopParam.parameter = commandp->executeStopParameters(); - LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam); - button->setFunctionName(commandp->executeFunctionName()); - LL_DEBUGS("UIUsage") << "button function name a -> " << commandp->executeFunctionName() << LL_ENDL; - LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam); - - button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2)); - button->setMouseUpCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, stop_func, _1, _2)); - } - else - { - button->setFunctionName(commandp->executeFunctionName()); - LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL; - button->setCommitCallback(executeParam); - } - - // Set up "is running" query callback - const std::string& isRunningFunction = commandp->isRunningFunctionName(); - if (isRunningFunction.length() > 0) - { - LLUICtrl::EnableCallbackParam isRunningParam; - isRunningParam.function_name = isRunningFunction; - isRunningParam.parameter = commandp->isRunningParameters(); - enable_signal_t::slot_type isRunningCB = initEnableCallback(isRunningParam); - - if (NULL == button->mIsRunningSignal) - { - button->mIsRunningSignal = new enable_signal_t(); - } - - button->mIsRunningSignal->connect(isRunningCB); - } - } - - // Drag and drop behavior must work also if provided in the Toybox and, potentially, any read-only toolbar - button->setStartDragCallback(mStartDragItemCallback); - button->setHandleDragCallback(mHandleDragItemCallback); - - button->setCommandId(id); - - return button; -} - -boost::signals2::connection connectSignal(LLToolBar::button_signal_t*& signal, const LLToolBar::button_signal_t::slot_type& cb) -{ - if (!signal) - { - signal = new LLToolBar::button_signal_t(); - } - - return signal->connect(cb); -} - -boost::signals2::connection LLToolBar::setButtonAddCallback(const button_signal_t::slot_type& cb) -{ - return connectSignal(mButtonAddSignal, cb); -} - -boost::signals2::connection LLToolBar::setButtonEnterCallback(const button_signal_t::slot_type& cb) -{ - return connectSignal(mButtonEnterSignal, cb); -} - -boost::signals2::connection LLToolBar::setButtonLeaveCallback(const button_signal_t::slot_type& cb) -{ - return connectSignal(mButtonLeaveSignal, cb); -} - -boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_signal_t::slot_type& cb) -{ - return connectSignal(mButtonRemoveSignal, cb); -} - -bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - // If we have a drop callback, that means that we can handle the drop - bool handled = mHandleDropCallback != nullptr; - - // if drop is set, it's time to call the callback to get the operation done - if (handled && drop) - { - handled = mHandleDropCallback(cargo_data, x, y, this); - } - - // We accept only single tool drop on toolbars - *accept = handled ? ACCEPT_YES_SINGLE : ACCEPT_NO; - - // We'll use that flag to change the visual aspect of the toolbar target on draw() - mDragAndDropTarget = false; - - // Convert drag position into insert position and rank - if (!isReadOnly() && handled && !drop) - { - if (cargo_type == DAD_WIDGET) - { - LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; - LLCommandId dragged_command(inv_item->getUUID()); - int orig_rank = getRankFromPosition(dragged_command); - mDragRank = getRankFromPosition(x, y); - // Don't DaD if we're dragging a command on itself - mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank - 1) == orig_rank))); - //LL_INFOS() << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << LL_ENDL; - /* Do the following if you want to animate the button itself - LLCommandId dragged_command(inv_item->getUUID()); - removeCommand(dragged_command); - addCommand(dragged_command,rank); - */ - } - else - { - handled = false; - } - } - - return handled; -} - -LLToolBarButton::LLToolBarButton(const Params& p) -: LLButton(p), - mMouseDownX(0), - mMouseDownY(0), - mWidthRange(p.button_width), - mDesiredHeight(p.desired_height), - mId(""), - mIsEnabledSignal(NULL), - mIsRunningSignal(NULL), - mIsStartingSignal(NULL), - mIsDragged(false), - mStartDragItemCallback(NULL), - mHandleDragItemCallback(NULL), - mOriginalImageSelected(p.image_selected), - mOriginalImageUnselected(p.image_unselected), - mOriginalImagePressed(p.image_pressed), - mOriginalImagePressedSelected(p.image_pressed_selected), - mOriginalLabelColor(p.label_color), - mOriginalLabelColorSelected(p.label_color_selected), - mOriginalImageOverlayColor(p.image_overlay_color), - mOriginalImageOverlaySelectedColor(p.image_overlay_selected_color) -{ -} - -LLToolBarButton::~LLToolBarButton() -{ - delete mIsEnabledSignal; - delete mIsRunningSignal; - delete mIsStartingSignal; -} - -bool LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask) -{ - mMouseDownX = x; - mMouseDownY = y; - return LLButton::handleMouseDown(x, y, mask); -} - -bool LLToolBarButton::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY); - if (mouse_distance_squared > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD - && hasMouseCapture() && - mStartDragItemCallback && mHandleDragItemCallback) - { - if (!mIsDragged) - { - mStartDragItemCallback(x, y, this); - mIsDragged = true; - handled = true; - } - else - { - handled = mHandleDragItemCallback(x, y, mId.uuid(), LLAssetType::AT_WIDGET); - } - } - else - { - handled = LLButton::handleHover(x, y, mask); - } - - return handled; -} - -void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask) -{ - LLUICtrl::onMouseEnter(x, y, mask); - - // Always highlight toolbar buttons, even if they are disabled - if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this) - { - mNeedsHighlight = true; - } - - LLToolBar* parent_toolbar = getParentByType(); - if (parent_toolbar && parent_toolbar->mButtonEnterSignal) - { - (*(parent_toolbar->mButtonEnterSignal))(this); - } -} - -void LLToolBarButton::onMouseLeave(S32 x, S32 y, MASK mask) -{ - LLButton::onMouseLeave(x, y, mask); - - LLToolBar* parent_toolbar = getParentByType(); - if (parent_toolbar && parent_toolbar->mButtonLeaveSignal) - { - (*(parent_toolbar->mButtonLeaveSignal))(this); - } -} - -void LLToolBarButton::onMouseCaptureLost() -{ - mIsDragged = false; -} - -void LLToolBarButton::onCommit() -{ - LLCommand* command = LLCommandManager::instance().getCommand(mId); - - if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters())) - { - LLButton::onCommit(); - } -} - -void LLToolBarButton::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent); -} - -void LLToolBarButton::setEnabled(bool enabled) -{ - if (enabled) - { - mImageSelected = mOriginalImageSelected; - mImageUnselected = mOriginalImageUnselected; - mImagePressed = mOriginalImagePressed; - mImagePressedSelected = mOriginalImagePressedSelected; - mUnselectedLabelColor = mOriginalLabelColor; - mSelectedLabelColor = mOriginalLabelColorSelected; - mImageOverlayColor = mOriginalImageOverlayColor; - mImageOverlaySelectedColor = mOriginalImageOverlaySelectedColor; - } - else - { - mImageSelected = mImageDisabledSelected; - mImageUnselected = mImageDisabled; - mImagePressed = mImageDisabled; - mImagePressedSelected = mImageDisabledSelected; - mUnselectedLabelColor = mDisabledLabelColor; - mSelectedLabelColor = mDisabledSelectedLabelColor; - mImageOverlayColor = mImageOverlayDisabledColor; - mImageOverlaySelectedColor = mImageOverlayDisabledColor; - } -} - -const std::string LLToolBarButton::getToolTip() const -{ - std::string tooltip; - - if (labelIsTruncated() || getCurrentLabel().empty()) - { - tooltip = LLTrans::getString(LLCommandManager::instance().getCommand(mId)->labelRef()) + " -- " + LLView::getToolTip(); - } - else - { - tooltip = LLView::getToolTip(); - } - - LLToolBar* parent_toolbar = getParentByType(); - if (parent_toolbar && parent_toolbar->mButtonTooltipSuffix.length() > 0) - { - tooltip = tooltip + "\n(" + parent_toolbar->mButtonTooltipSuffix + ")"; - } - - return tooltip; -} - -void LLToolBar::LLCenterLayoutPanel::handleReshape(const LLRect& rect, bool by_user) -{ - LLLayoutPanel::handleReshape(rect, by_user); - - if (!mReshapeCallback.empty()) - { - LLRect r; - localRectToOtherView(mButtonPanel->getRect(), &r, gFloaterView); - r.stretch(FLOATER_MIN_VISIBLE_PIXELS); - mReshapeCallback(mLocationId, r); - } -} +/** + * @file lltoolbar.cpp + * @author Richard Nelson + * @brief User customizable toolbar class + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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 "lltoolbar.h" + +#include "llcommandmanager.h" +#include "llmenugl.h" +#include "lltrans.h" +#include "llinventory.h" +#include "lliconctrl.h" + +// uncomment this and remove the one in llui.cpp when there is an external reference to this translation unit +// thanks, MSVC! +//static LLDefaultChildRegistry::Register r1("toolbar"); + +namespace LLToolBarEnums +{ + LLView::EOrientation getOrientation(SideType sideType) + { + LLView::EOrientation orientation = LLLayoutStack::HORIZONTAL; + + if ((sideType == SIDE_LEFT) || (sideType == SIDE_RIGHT)) + { + orientation = LLLayoutStack::VERTICAL; + } + + return orientation; + } +} + +using namespace LLToolBarEnums; + + +namespace LLInitParam +{ + void TypeValues::declareValues() + { + declare("icons_with_text", BTNTYPE_ICONS_WITH_TEXT); + declare("icons_only", BTNTYPE_ICONS_ONLY); + } + + void TypeValues::declareValues() + { + declare("bottom", SIDE_BOTTOM); + declare("left", SIDE_LEFT); + declare("right", SIDE_RIGHT); + declare("top", SIDE_TOP); + } +} + +LLToolBar::Params::Params() +: button_display_mode("button_display_mode"), + commands("command"), + side("side", SIDE_TOP), + button_icon("button_icon"), + button_icon_and_text("button_icon_and_text"), + read_only("read_only", false), + wrap("wrap", true), + pad_left("pad_left"), + pad_top("pad_top"), + pad_right("pad_right"), + pad_bottom("pad_bottom"), + pad_between("pad_between"), + min_girth("min_girth"), + button_panel("button_panel") +{} + +LLToolBar::LLToolBar(const LLToolBar::Params& p) +: LLUICtrl(p), + mReadOnly(p.read_only), + mButtonType(p.button_display_mode), + mSideType(p.side), + mWrap(p.wrap), + mNeedsLayout(false), + mModified(false), + mButtonPanel(NULL), + mCenteringStack(NULL), + mPadLeft(p.pad_left), + mPadRight(p.pad_right), + mPadTop(p.pad_top), + mPadBottom(p.pad_bottom), + mPadBetween(p.pad_between), + mMinGirth(p.min_girth), + mPopupMenuHandle(), + mRightMouseTargetButton(NULL), + mStartDragItemCallback(NULL), + mHandleDragItemCallback(NULL), + mHandleDropCallback(NULL), + mButtonAddSignal(NULL), + mButtonEnterSignal(NULL), + mButtonLeaveSignal(NULL), + mButtonRemoveSignal(NULL), + mDragAndDropTarget(false), + mCaretIcon(NULL), + mCenterPanel(NULL) +{ + mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_WITH_TEXT] = p.button_icon_and_text; + mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_ONLY] = p.button_icon; +} + +LLToolBar::~LLToolBar() +{ + auto menu = mPopupMenuHandle.get(); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } + delete mButtonAddSignal; + delete mButtonEnterSignal; + delete mButtonLeaveSignal; + delete mButtonRemoveSignal; +} + +void LLToolBar::createContextMenu() +{ + if (!mPopupMenuHandle.get()) + { + // Setup bindings specific to this instance for the context menu options + + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit_reg; + commit_reg.add("Toolbars.EnableSetting", boost::bind(&LLToolBar::onSettingEnable, this, _2)); + commit_reg.add("Toolbars.RemoveSelectedCommand", boost::bind(&LLToolBar::onRemoveSelectedCommand, this)); + + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_reg; + enable_reg.add("Toolbars.CheckSetting", boost::bind(&LLToolBar::isSettingChecked, this, _2)); + + // Create the context menu + llassert(LLMenuGL::sMenuContainer != NULL); + LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile("menu_toolbars.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + + if (menu) + { + menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); + mPopupMenuHandle = menu->getHandle(); + mRemoveButtonHandle = menu->getChild("Remove button")->getHandle(); + } + else + { + LL_WARNS() << "Unable to load toolbars context menu." << LL_ENDL; + } + } + + if (mRemoveButtonHandle.get()) + { + // Disable/Enable the "Remove button" menu item depending on whether or not a button was clicked + mRemoveButtonHandle.get()->setEnabled(mRightMouseTargetButton != NULL); + } +} + +void LLToolBar::initFromParams(const LLToolBar::Params& p) +{ + // Initialize the base object + LLUICtrl::initFromParams(p); + + LLView::EOrientation orientation = getOrientation(p.side); + + LLLayoutStack::Params centering_stack_p; + centering_stack_p.name = "centering_stack"; + centering_stack_p.rect = getLocalRect(); + centering_stack_p.follows.flags = FOLLOWS_ALL; + centering_stack_p.orientation = orientation; + centering_stack_p.mouse_opaque = false; + + mCenteringStack = LLUICtrlFactory::create(centering_stack_p); + addChild(mCenteringStack); + + LLLayoutPanel::Params border_panel_p; + border_panel_p.name = "border_panel"; + border_panel_p.rect = getLocalRect(); + border_panel_p.auto_resize = true; + border_panel_p.user_resize = false; + border_panel_p.mouse_opaque = false; + + mCenteringStack->addChild(LLUICtrlFactory::create(border_panel_p)); + + LLLayoutPanel::Params center_panel_p; + center_panel_p.name = "center_panel"; + center_panel_p.rect = getLocalRect(); + center_panel_p.auto_resize = false; + center_panel_p.user_resize = false; + center_panel_p.mouse_opaque = false; + mCenterPanel = LLUICtrlFactory::create(center_panel_p); + mCenteringStack->addChild(mCenterPanel); + + LLPanel::Params button_panel_p(p.button_panel); + button_panel_p.rect = mCenterPanel->getLocalRect(); + button_panel_p.follows.flags = FOLLOWS_BOTTOM|FOLLOWS_LEFT; + mButtonPanel = LLUICtrlFactory::create(button_panel_p); + mCenterPanel->setButtonPanel(mButtonPanel); + mCenterPanel->addChild(mButtonPanel); + + mCenteringStack->addChild(LLUICtrlFactory::create(border_panel_p)); + + for (const auto& id : p.commands) + { + addCommand(id); + } + + mNeedsLayout = true; +} + +bool LLToolBar::addCommand(const LLCommandId& commandId, int rank) +{ + LLCommand * command = LLCommandManager::instance().getCommand(commandId); + if (!command) return false; + + // Create the button and do the things that don't need ordering + LLToolBarButton* button = createButton(commandId); + mButtonPanel->addChild(button); + mButtonMap.insert(std::make_pair(commandId.uuid(), button)); + + // Insert the command and button in the right place in their respective lists + if ((rank >= mButtonCommands.size()) || (rank == RANK_NONE)) + { + // In that case, back load + mButtonCommands.push_back(command->id()); + mButtons.push_back(button); + } + else + { + // Insert in place: iterate to the right spot... + std::list::iterator it_button = mButtons.begin(); + command_id_list_t::iterator it_command = mButtonCommands.begin(); + while (rank > 0) + { + ++it_button; + ++it_command; + rank--; + } + // ...then insert + mButtonCommands.insert(it_command, command->id()); + mButtons.insert(it_button,button); + } + + mNeedsLayout = true; + + updateLayoutAsNeeded(); + + + if (mButtonAddSignal) + { + (*mButtonAddSignal)(button); + } + + return true; +} + +// Remove a command from the list +// Returns the rank of the command in the original list so that doing addCommand(id,rank) right after +// a removeCommand(id) would leave the list unchanged. +// Returns RANK_NONE if the command is not found in the list +int LLToolBar::removeCommand(const LLCommandId& commandId) +{ + if (!hasCommand(commandId)) return RANK_NONE; + + // First erase the map record + command_id_map::iterator it = mButtonMap.find(commandId.uuid()); + mButtonMap.erase(it); + + // Now iterate on the commands and buttons to identify the relevant records + int rank = 0; + std::list::iterator it_button = mButtons.begin(); + command_id_list_t::iterator it_command = mButtonCommands.begin(); + while (*it_command != commandId) + { + ++it_button; + ++it_command; + ++rank; + } + + if (mButtonRemoveSignal) + { + (*mButtonRemoveSignal)(*it_button); + } + + // Delete the button and erase the command and button records + delete (*it_button); + mButtonCommands.erase(it_command); + mButtons.erase(it_button); + + mNeedsLayout = true; + + return rank; +} + +void LLToolBar::clearCommandsList() +{ + // Clears the commands list + mButtonCommands.clear(); + // This will clear the buttons + createButtons(); +} + +bool LLToolBar::hasCommand(const LLCommandId& commandId) const +{ + if (commandId != LLCommandId::null) + { + command_id_map::const_iterator it = mButtonMap.find(commandId.uuid()); + return (it != mButtonMap.end()); + } + + return false; +} + +bool LLToolBar::enableCommand(const LLCommandId& commandId, bool enabled) +{ + LLButton * command_button = NULL; + + if (commandId != LLCommandId::null) + { + command_id_map::iterator it = mButtonMap.find(commandId.uuid()); + if (it != mButtonMap.end()) + { + command_button = it->second; + command_button->setEnabled(enabled); + } + } + + return (command_button != NULL); +} + +bool LLToolBar::stopCommandInProgress(const LLCommandId& commandId) +{ + // + // Note from Leslie: + // + // This implementation was largely put in place to handle EXP-1348 which is related to + // dragging and dropping the "speak" button. The "speak" button can be in one of two + // modes, i.e., either a toggle action or a push-to-talk action. Because of this it + // responds to mouse down and mouse up in different ways, based on which behavior the + // button is currently set to obey. This was the simplest way of getting the button + // to turn off the microphone for both behaviors without risking duplicate state. + // + + LLToolBarButton * command_button = NULL; + + if (commandId != LLCommandId::null) + { + LLCommand* command = LLCommandManager::instance().getCommand(commandId); + llassert(command); + + // If this command has an explicit function for execution stop + if (command->executeStopFunctionName().length() > 0) + { + command_id_map::iterator it = mButtonMap.find(commandId.uuid()); + if (it != mButtonMap.end()) + { + command_button = it->second; + llassert(command_button->mIsRunningSignal); + + // Check to see if it is running + if ((*command_button->mIsRunningSignal)(command_button, command->isRunningParameters())) + { + // Trigger an additional button commit, which calls mouse down, mouse up and commit + command_button->onCommit(); + } + } + } + } + + return (command_button != NULL); +} + +bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing/* = false */) +{ + LLButton * command_button = NULL; + + if (commandId != LLCommandId::null) + { + command_id_map::iterator it = mButtonMap.find(commandId.uuid()); + if (it != mButtonMap.end()) + { + command_button = it->second; + command_button->setFlashing((bool)(flash),(bool)(force_flashing)); + } + } + + return (command_button != NULL); +} + +bool LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLRect button_panel_rect; + mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this); + bool handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y); + + if (handle_it_here) + { + // Determine which button the mouse was over during the click in case the context menu action + // is intended to affect the button. + mRightMouseTargetButton = NULL; + for (LLToolBarButton* button : mButtons) + { + LLRect button_rect; + button->localRectToOtherView(button->getLocalRect(), &button_rect, this); + + if (button_rect.pointInRect(x, y)) + { + mRightMouseTargetButton = button; + break; + } + } + + createContextMenu(); + + LLContextMenu * menu = (LLContextMenu *) mPopupMenuHandle.get(); + + if (menu) + { + menu->show(x, y); + + LLMenuGL::showPopup(this, menu, x, y); + } + } + + return handle_it_here; +} + +bool LLToolBar::isSettingChecked(const LLSD& userdata) +{ + bool retval = false; + + const std::string setting_name = userdata.asString(); + + if (setting_name == "icons_with_text") + { + retval = (mButtonType == BTNTYPE_ICONS_WITH_TEXT); + } + else if (setting_name == "icons_only") + { + retval = (mButtonType == BTNTYPE_ICONS_ONLY); + } + + return retval; +} + +void LLToolBar::onSettingEnable(const LLSD& userdata) +{ + llassert(!mReadOnly); + + const std::string setting_name = userdata.asString(); + + if (setting_name == "icons_with_text") + { + setButtonType(BTNTYPE_ICONS_WITH_TEXT); + } + else if (setting_name == "icons_only") + { + setButtonType(BTNTYPE_ICONS_ONLY); + } +} + +void LLToolBar::onRemoveSelectedCommand() +{ + llassert(!mReadOnly); + + if (mRightMouseTargetButton) + { + removeCommand(mRightMouseTargetButton->getCommandId()); + + mRightMouseTargetButton = NULL; + } +} + +void LLToolBar::setButtonType(LLToolBarEnums::ButtonType button_type) +{ + bool regenerate_buttons = (mButtonType != button_type); + + mButtonType = button_type; + + if (regenerate_buttons) + { + createButtons(); + } +} + +void LLToolBar::resizeButtonsInRow(std::vector& buttons_in_row, S32 max_row_girth) +{ + // make buttons in current row all same girth + for (LLToolBarButton* button : buttons_in_row) + { + if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL) + { + button->reshape(button->mWidthRange.clamp(button->getRect().getWidth()), max_row_girth); + } + else // VERTICAL + { + button->reshape(max_row_girth, button->getRect().getHeight()); + } + } +} + +// Returns the position of the coordinates as a rank in the button list. +// The rank is the position a tool dropped in (x,y) would assume in the button list. +// The returned value is between 0 and mButtons.size(), 0 being the first element to the left +// (or top) and mButtons.size() the last one to the right (or bottom). +// Various drag data are stored in the toolbar object though are not exposed outside (and shouldn't). +int LLToolBar::getRankFromPosition(S32 x, S32 y) +{ + if (mButtons.empty()) + { + return RANK_NONE; + } + + int rank = 0; + + // Convert the toolbar coord into button panel coords + LLView::EOrientation orientation = getOrientation(mSideType); + S32 button_panel_x = 0; + S32 button_panel_y = 0; + localPointToOtherView(x, y, &button_panel_x, &button_panel_y, mButtonPanel); + S32 dx = x - button_panel_x; + S32 dy = y - button_panel_y; + + // Simply compare the passed coord with the buttons outbound box + padding + std::list::iterator it_button = mButtons.begin(); + std::list::iterator end_button = mButtons.end(); + LLRect button_rect; + while (it_button != end_button) + { + button_rect = (*it_button)->getRect(); + S32 point_x = button_rect.mRight + mPadRight; + S32 point_y = button_rect.mBottom - mPadBottom; + + if ((button_panel_x < point_x) && (button_panel_y > point_y)) + { + break; + } + rank++; + ++it_button; + } + + // Update the passed coordinates to the hit button relevant corner + // (different depending on toolbar orientation) + if (rank < mButtons.size()) + { + if (orientation == LLLayoutStack::HORIZONTAL) + { + // Horizontal + S32 mid_point = (button_rect.mRight + button_rect.mLeft) / 2; + if (button_panel_x < mid_point) + { + mDragx = button_rect.mLeft - mPadLeft; + mDragy = button_rect.mTop + mPadTop; + } + else + { + rank++; + mDragx = button_rect.mRight + mPadRight - 1; + mDragy = button_rect.mTop + mPadTop; + } + } + else + { + // Vertical + S32 mid_point = (button_rect.mTop + button_rect.mBottom) / 2; + if (button_panel_y > mid_point) + { + mDragx = button_rect.mLeft - mPadLeft; + mDragy = button_rect.mTop + mPadTop; + } + else + { + rank++; + mDragx = button_rect.mLeft - mPadLeft; + mDragy = button_rect.mBottom - mPadBottom + 1; + } + } + } + else + { + // We hit passed the end of the list so put the insertion point at the end + if (orientation == LLLayoutStack::HORIZONTAL) + { + mDragx = button_rect.mRight + mPadRight; + mDragy = button_rect.mTop + mPadTop; + } + else + { + mDragx = button_rect.mLeft - mPadLeft; + mDragy = button_rect.mBottom - mPadBottom; + } + } + + // Update the "girth" of the caret, i.e. the width or height (depending of orientation) + if (orientation == LLLayoutStack::HORIZONTAL) + { + mDragGirth = button_rect.getHeight() + mPadBottom + mPadTop; + } + else + { + mDragGirth = button_rect.getWidth() + mPadLeft + mPadRight; + } + + // The delta account for the coord model change (i.e. convert back to toolbar coord) + mDragx += dx; + mDragy += dy; + + return rank; +} + +int LLToolBar::getRankFromPosition(const LLCommandId& id) +{ + if (!hasCommand(id)) + { + return RANK_NONE; + } + int rank = 0; + std::list::iterator it_button = mButtons.begin(); + std::list::iterator end_button = mButtons.end(); + while (it_button != end_button) + { + if ((*it_button)->mId == id) + { + break; + } + rank++; + ++it_button; + } + return rank; +} + +void LLToolBar::updateLayoutAsNeeded() +{ + if (!mNeedsLayout) return; + + LLView::EOrientation orientation = getOrientation(mSideType); + + // our terminology for orientation-agnostic layout is such that + // length refers to a distance in the direction we stack the buttons + // and girth refers to a distance in the direction buttons wrap + S32 max_row_girth = 0; + S32 max_row_length = 0; + + S32 max_length; + S32 cur_start; + S32 cur_row ; + S32 row_pad_start; + S32 row_pad_end; + S32 girth_pad_end; + S32 row_running_length; + + if (orientation == LLLayoutStack::HORIZONTAL) + { + max_length = getRect().getWidth() - mPadLeft - mPadRight; + row_pad_start = mPadLeft; + row_pad_end = mPadRight; + cur_row = mPadTop; + girth_pad_end = mPadBottom; + } + else // VERTICAL + { + max_length = getRect().getHeight() - mPadTop - mPadBottom; + row_pad_start = mPadTop; + row_pad_end = mPadBottom; + cur_row = mPadLeft; + girth_pad_end = mPadRight; + } + + row_running_length = row_pad_start; + cur_start = row_pad_start; + + + LLRect panel_rect = mButtonPanel->getLocalRect(); + + std::vector buttons_in_row; + + for (LLToolBarButton* button : mButtons) + { + button->reshape(button->mWidthRange.getMin(), button->mDesiredHeight); + button->autoResize(); + + S32 button_clamped_width = button->mWidthRange.clamp(button->getRect().getWidth()); + S32 button_length = (orientation == LLLayoutStack::HORIZONTAL) + ? button_clamped_width + : button->getRect().getHeight(); + S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL) + ? button->getRect().getHeight() + : button_clamped_width; + + // wrap if needed + if (mWrap + && row_running_length + button_length > max_length // out of room... + && cur_start != row_pad_start) // ...and not first button in row + { + if (orientation == LLLayoutStack::VERTICAL) + { // row girth (width in this case) is clamped to allowable button widths + max_row_girth = button->mWidthRange.clamp(max_row_girth); + } + + // make buttons in current row all same girth + resizeButtonsInRow(buttons_in_row, max_row_girth); + buttons_in_row.clear(); + + max_row_length = llmax(max_row_length, row_running_length); + row_running_length = row_pad_start; + cur_start = row_pad_start; + cur_row += max_row_girth + mPadBetween; + max_row_girth = 0; + } + + LLRect button_rect; + if (orientation == LLLayoutStack::HORIZONTAL) + { + button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight()); + } + else // VERTICAL + { + button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight()); + } + button->setShape(button_rect); + + buttons_in_row.push_back(button); + + row_running_length += button_length + mPadBetween; + cur_start = row_running_length; + max_row_girth = llmax(button_girth, max_row_girth); + } + + // final resizing in "girth" direction + S32 total_girth = cur_row // current row position... + + max_row_girth // ...incremented by size of final row... + + girth_pad_end; // ...plus padding reserved on end + total_girth = llmax(total_girth,mMinGirth); + + max_row_length = llmax(max_row_length, row_running_length - mPadBetween + row_pad_end); + + resizeButtonsInRow(buttons_in_row, max_row_girth); + + // grow and optionally shift toolbar to accommodate buttons + if (orientation == LLLayoutStack::HORIZONTAL) + { + if (mSideType == SIDE_TOP) + { // shift down to maintain top edge + translate(0, getRect().getHeight() - total_girth); + } + + reshape(getRect().getWidth(), total_girth); + mButtonPanel->reshape(max_row_length, total_girth); + } + else // VERTICAL + { + if (mSideType == SIDE_RIGHT) + { // shift left to maintain right edge + translate(getRect().getWidth() - total_girth, 0); + } + + reshape(total_girth, getRect().getHeight()); + mButtonPanel->reshape(total_girth, max_row_length); + } + + // make parent fit button panel + mButtonPanel->getParent()->setShape(mButtonPanel->getLocalRect()); + + // re-center toolbar buttons + mCenteringStack->updateLayout(); + + if (!mButtons.empty()) + { + mButtonPanel->setVisible(true); + mButtonPanel->setMouseOpaque(true); + } + + // don't clear flag until after we've resized ourselves, to avoid laying out every frame + mNeedsLayout = false; +} + + +void LLToolBar::draw() +{ + if (mButtons.empty()) + { + mButtonPanel->setVisible(false); + mButtonPanel->setMouseOpaque(false); + } + else + { + mButtonPanel->setVisible(true); + mButtonPanel->setMouseOpaque(true); + } + + // Update enable/disable state and highlight state for editable toolbars + if (!mReadOnly) + { + for (toolbar_button_list::iterator btn_it = mButtons.begin(); btn_it != mButtons.end(); ++btn_it) + { + LLToolBarButton* btn = *btn_it; + LLCommand* command = LLCommandManager::instance().getCommand(btn->mId); + + if (command && btn->mIsEnabledSignal) + { + const bool button_command_enabled = (*btn->mIsEnabledSignal)(btn, command->isEnabledParameters()); + btn->setEnabled(button_command_enabled); + } + + if (command && btn->mIsRunningSignal) + { + const bool button_command_running = (*btn->mIsRunningSignal)(btn, command->isRunningParameters()); + btn->setToggleState(button_command_running); + } + } + } + + updateLayoutAsNeeded(); + // rect may have shifted during layout + LLUI::popMatrix(); + LLUI::pushMatrix(); + LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); + + // Position the caret + if (!mCaretIcon) + { + mCaretIcon = getChild("caret"); + } + + LLIconCtrl* caret = mCaretIcon; + caret->setVisible(false); + if (mDragAndDropTarget && !mButtonCommands.empty()) + { + LLRect caret_rect = caret->getRect(); + if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL) + { + caret->setRect(LLRect(mDragx-caret_rect.getWidth()/2+1, + mDragy, + mDragx+caret_rect.getWidth()/2+1, + mDragy-mDragGirth)); + } + else + { + caret->setRect(LLRect(mDragx, + mDragy+caret_rect.getHeight()/2, + mDragx+mDragGirth, + mDragy-caret_rect.getHeight()/2)); + } + caret->setVisible(true); + } + + LLUICtrl::draw(); + caret->setVisible(false); + mDragAndDropTarget = false; +} + +void LLToolBar::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + mNeedsLayout = true; +} + +void LLToolBar::createButtons() +{ + std::set set_flashing; + + for (LLToolBarButton* button : mButtons) + { + if (button->getFlashTimer() && button->getFlashTimer()->isFlashingInProgress()) + { + set_flashing.insert(button->getCommandId().uuid()); + } + + if (mButtonRemoveSignal) + { + (*mButtonRemoveSignal)(button); + } + + delete button; + } + mButtons.clear(); + mButtonMap.clear(); + mRightMouseTargetButton = NULL; + + for (const LLCommandId& command_id : mButtonCommands) + { + LLToolBarButton* button = createButton(command_id); + mButtons.push_back(button); + mButtonPanel->addChild(button); + mButtonMap.insert(std::make_pair(command_id.uuid(), button)); + + if (mButtonAddSignal) + { + (*mButtonAddSignal)(button); + } + + if (set_flashing.find(button->getCommandId().uuid()) != set_flashing.end()) + { + button->setFlashing(true); + } + } + mNeedsLayout = true; +} + +void LLToolBarButton::callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param ) +{ + LLCommand* command = LLCommandManager::instance().getCommand(mId); + + if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters())) + { + commit(ctrl, param); + } +} + +LLToolBarButton* LLToolBar::createButton(const LLCommandId& id) +{ + LLCommand* commandp = LLCommandManager::instance().getCommand(id); + if (!commandp) return NULL; + + LLToolBarButton::Params button_p; + button_p.name = commandp->name(); + button_p.label = LLTrans::getString(commandp->labelRef()); + button_p.tool_tip = LLTrans::getString(commandp->tooltipRef()); + button_p.image_overlay = LLUI::getUIImage(commandp->icon()); + button_p.button_flash_enable = commandp->isFlashingAllowed(); + button_p.overwriteFrom(mButtonParams[mButtonType]); + LLToolBarButton* button = LLUICtrlFactory::create(button_p); + + if (!mReadOnly) + { + enable_callback_t isEnabledCB; + + const std::string& isEnabledFunction = commandp->isEnabledFunctionName(); + if (isEnabledFunction.length() > 0) + { + LLUICtrl::EnableCallbackParam isEnabledParam; + isEnabledParam.function_name = isEnabledFunction; + isEnabledParam.parameter = commandp->isEnabledParameters(); + isEnabledCB = initEnableCallback(isEnabledParam); + + if (NULL == button->mIsEnabledSignal) + { + button->mIsEnabledSignal = new enable_signal_t(); + } + + button->mIsEnabledSignal->connect(isEnabledCB); + } + + LLUICtrl::CommitCallbackParam executeParam; + executeParam.function_name = commandp->executeFunctionName(); + executeParam.parameter = commandp->executeParameters(); + + // If we have a "stop" function then we map the command to mouse down / mouse up otherwise commit + const std::string& executeStopFunction = commandp->executeStopFunctionName(); + if (executeStopFunction.length() > 0) + { + LLUICtrl::CommitCallbackParam executeStopParam; + executeStopParam.function_name = executeStopFunction; + executeStopParam.parameter = commandp->executeStopParameters(); + LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam); + button->setFunctionName(commandp->executeFunctionName()); + LL_DEBUGS("UIUsage") << "button function name a -> " << commandp->executeFunctionName() << LL_ENDL; + LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam); + + button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2)); + button->setMouseUpCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, stop_func, _1, _2)); + } + else + { + button->setFunctionName(commandp->executeFunctionName()); + LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL; + button->setCommitCallback(executeParam); + } + + // Set up "is running" query callback + const std::string& isRunningFunction = commandp->isRunningFunctionName(); + if (isRunningFunction.length() > 0) + { + LLUICtrl::EnableCallbackParam isRunningParam; + isRunningParam.function_name = isRunningFunction; + isRunningParam.parameter = commandp->isRunningParameters(); + enable_signal_t::slot_type isRunningCB = initEnableCallback(isRunningParam); + + if (NULL == button->mIsRunningSignal) + { + button->mIsRunningSignal = new enable_signal_t(); + } + + button->mIsRunningSignal->connect(isRunningCB); + } + } + + // Drag and drop behavior must work also if provided in the Toybox and, potentially, any read-only toolbar + button->setStartDragCallback(mStartDragItemCallback); + button->setHandleDragCallback(mHandleDragItemCallback); + + button->setCommandId(id); + + return button; +} + +boost::signals2::connection connectSignal(LLToolBar::button_signal_t*& signal, const LLToolBar::button_signal_t::slot_type& cb) +{ + if (!signal) + { + signal = new LLToolBar::button_signal_t(); + } + + return signal->connect(cb); +} + +boost::signals2::connection LLToolBar::setButtonAddCallback(const button_signal_t::slot_type& cb) +{ + return connectSignal(mButtonAddSignal, cb); +} + +boost::signals2::connection LLToolBar::setButtonEnterCallback(const button_signal_t::slot_type& cb) +{ + return connectSignal(mButtonEnterSignal, cb); +} + +boost::signals2::connection LLToolBar::setButtonLeaveCallback(const button_signal_t::slot_type& cb) +{ + return connectSignal(mButtonLeaveSignal, cb); +} + +boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_signal_t::slot_type& cb) +{ + return connectSignal(mButtonRemoveSignal, cb); +} + +bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + // If we have a drop callback, that means that we can handle the drop + bool handled = mHandleDropCallback != nullptr; + + // if drop is set, it's time to call the callback to get the operation done + if (handled && drop) + { + handled = mHandleDropCallback(cargo_data, x, y, this); + } + + // We accept only single tool drop on toolbars + *accept = handled ? ACCEPT_YES_SINGLE : ACCEPT_NO; + + // We'll use that flag to change the visual aspect of the toolbar target on draw() + mDragAndDropTarget = false; + + // Convert drag position into insert position and rank + if (!isReadOnly() && handled && !drop) + { + if (cargo_type == DAD_WIDGET) + { + LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + LLCommandId dragged_command(inv_item->getUUID()); + int orig_rank = getRankFromPosition(dragged_command); + mDragRank = getRankFromPosition(x, y); + // Don't DaD if we're dragging a command on itself + mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank - 1) == orig_rank))); + //LL_INFOS() << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << LL_ENDL; + /* Do the following if you want to animate the button itself + LLCommandId dragged_command(inv_item->getUUID()); + removeCommand(dragged_command); + addCommand(dragged_command,rank); + */ + } + else + { + handled = false; + } + } + + return handled; +} + +LLToolBarButton::LLToolBarButton(const Params& p) +: LLButton(p), + mMouseDownX(0), + mMouseDownY(0), + mWidthRange(p.button_width), + mDesiredHeight(p.desired_height), + mId(""), + mIsEnabledSignal(NULL), + mIsRunningSignal(NULL), + mIsStartingSignal(NULL), + mIsDragged(false), + mStartDragItemCallback(NULL), + mHandleDragItemCallback(NULL), + mOriginalImageSelected(p.image_selected), + mOriginalImageUnselected(p.image_unselected), + mOriginalImagePressed(p.image_pressed), + mOriginalImagePressedSelected(p.image_pressed_selected), + mOriginalLabelColor(p.label_color), + mOriginalLabelColorSelected(p.label_color_selected), + mOriginalImageOverlayColor(p.image_overlay_color), + mOriginalImageOverlaySelectedColor(p.image_overlay_selected_color) +{ +} + +LLToolBarButton::~LLToolBarButton() +{ + delete mIsEnabledSignal; + delete mIsRunningSignal; + delete mIsStartingSignal; +} + +bool LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask) +{ + mMouseDownX = x; + mMouseDownY = y; + return LLButton::handleMouseDown(x, y, mask); +} + +bool LLToolBarButton::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY); + if (mouse_distance_squared > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD + && hasMouseCapture() && + mStartDragItemCallback && mHandleDragItemCallback) + { + if (!mIsDragged) + { + mStartDragItemCallback(x, y, this); + mIsDragged = true; + handled = true; + } + else + { + handled = mHandleDragItemCallback(x, y, mId.uuid(), LLAssetType::AT_WIDGET); + } + } + else + { + handled = LLButton::handleHover(x, y, mask); + } + + return handled; +} + +void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask) +{ + LLUICtrl::onMouseEnter(x, y, mask); + + // Always highlight toolbar buttons, even if they are disabled + if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this) + { + mNeedsHighlight = true; + } + + LLToolBar* parent_toolbar = getParentByType(); + if (parent_toolbar && parent_toolbar->mButtonEnterSignal) + { + (*(parent_toolbar->mButtonEnterSignal))(this); + } +} + +void LLToolBarButton::onMouseLeave(S32 x, S32 y, MASK mask) +{ + LLButton::onMouseLeave(x, y, mask); + + LLToolBar* parent_toolbar = getParentByType(); + if (parent_toolbar && parent_toolbar->mButtonLeaveSignal) + { + (*(parent_toolbar->mButtonLeaveSignal))(this); + } +} + +void LLToolBarButton::onMouseCaptureLost() +{ + mIsDragged = false; +} + +void LLToolBarButton::onCommit() +{ + LLCommand* command = LLCommandManager::instance().getCommand(mId); + + if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters())) + { + LLButton::onCommit(); + } +} + +void LLToolBarButton::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent); +} + +void LLToolBarButton::setEnabled(bool enabled) +{ + if (enabled) + { + mImageSelected = mOriginalImageSelected; + mImageUnselected = mOriginalImageUnselected; + mImagePressed = mOriginalImagePressed; + mImagePressedSelected = mOriginalImagePressedSelected; + mUnselectedLabelColor = mOriginalLabelColor; + mSelectedLabelColor = mOriginalLabelColorSelected; + mImageOverlayColor = mOriginalImageOverlayColor; + mImageOverlaySelectedColor = mOriginalImageOverlaySelectedColor; + } + else + { + mImageSelected = mImageDisabledSelected; + mImageUnselected = mImageDisabled; + mImagePressed = mImageDisabled; + mImagePressedSelected = mImageDisabledSelected; + mUnselectedLabelColor = mDisabledLabelColor; + mSelectedLabelColor = mDisabledSelectedLabelColor; + mImageOverlayColor = mImageOverlayDisabledColor; + mImageOverlaySelectedColor = mImageOverlayDisabledColor; + } +} + +const std::string LLToolBarButton::getToolTip() const +{ + std::string tooltip; + + if (labelIsTruncated() || getCurrentLabel().empty()) + { + tooltip = LLTrans::getString(LLCommandManager::instance().getCommand(mId)->labelRef()) + " -- " + LLView::getToolTip(); + } + else + { + tooltip = LLView::getToolTip(); + } + + LLToolBar* parent_toolbar = getParentByType(); + if (parent_toolbar && parent_toolbar->mButtonTooltipSuffix.length() > 0) + { + tooltip = tooltip + "\n(" + parent_toolbar->mButtonTooltipSuffix + ")"; + } + + return tooltip; +} + +void LLToolBar::LLCenterLayoutPanel::handleReshape(const LLRect& rect, bool by_user) +{ + LLLayoutPanel::handleReshape(rect, by_user); + + if (!mReshapeCallback.empty()) + { + LLRect r; + localRectToOtherView(mButtonPanel->getRect(), &r, gFloaterView); + r.stretch(FLOATER_MIN_VISIBLE_PIXELS); + mReshapeCallback(mLocationId, r); + } +} diff --git a/indra/llui/lltoolbar.h b/indra/llui/lltoolbar.h index 4dee33f4ab..e8ec1e41df 100644 --- a/indra/llui/lltoolbar.h +++ b/indra/llui/lltoolbar.h @@ -1,331 +1,331 @@ -/** - * @file lltoolbar.h - * @author Richard Nelson - * @brief User customizable toolbar class - * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, 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_LLTOOLBAR_H -#define LL_LLTOOLBAR_H - -#include "llbutton.h" -#include "llcommandmanager.h" -#include "lllayoutstack.h" -#include "lluictrl.h" -#include "llcommandmanager.h" -#include "llassettype.h" - -class LLToolBar; -class LLToolBarButton; -class LLIconCtrl; - -typedef boost::function tool_startdrag_callback_t; -typedef boost::function tool_handledrag_callback_t; -typedef boost::function tool_handledrop_callback_t; - -class LLToolBarButton : public LLButton -{ - friend class LLToolBar; -public: - struct Params : public LLInitParam::Block - { - Optional button_width; - Optional desired_height; - - Params() - : button_width("button_width"), - desired_height("desired_height", 20) - {} - - }; - - LLToolBarButton(const Params& p); - ~LLToolBarButton(); - - bool handleMouseDown(S32 x, S32 y, MASK mask); - bool handleHover(S32 x, S32 y, MASK mask); - - void reshape(S32 width, S32 height, bool called_from_parent = true); - void setEnabled(bool enabled); - void setCommandId(const LLCommandId& id) { mId = id; } - LLCommandId getCommandId() { return mId; } - - void setStartDragCallback(tool_startdrag_callback_t cb) { mStartDragItemCallback = cb; } - void setHandleDragCallback(tool_handledrag_callback_t cb) { mHandleDragItemCallback = cb; } - - void onMouseEnter(S32 x, S32 y, MASK mask); - void onMouseLeave(S32 x, S32 y, MASK mask); - void onMouseCaptureLost(); - - void onCommit(); - - virtual const std::string getToolTip() const; - -private: - void callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param ); - - LLCommandId mId; - S32 mMouseDownX; - S32 mMouseDownY; - LLUI::RangeS32 mWidthRange; - S32 mDesiredHeight; - bool mIsDragged; - tool_startdrag_callback_t mStartDragItemCallback; - tool_handledrag_callback_t mHandleDragItemCallback; - - enable_signal_t* mIsEnabledSignal; - enable_signal_t* mIsRunningSignal; - enable_signal_t* mIsStartingSignal; - LLPointer mOriginalImageSelected, - mOriginalImageUnselected, - mOriginalImagePressed, - mOriginalImagePressedSelected; - LLUIColor mOriginalLabelColor, - mOriginalLabelColorSelected, - mOriginalImageOverlayColor, - mOriginalImageOverlaySelectedColor; -}; - - -namespace LLToolBarEnums -{ - enum ButtonType - { - BTNTYPE_ICONS_WITH_TEXT = 0, - BTNTYPE_ICONS_ONLY, - - BTNTYPE_COUNT - }; - - enum SideType - { - SIDE_BOTTOM, - SIDE_LEFT, - SIDE_RIGHT, - SIDE_TOP, - }; - - enum EToolBarLocation - { - TOOLBAR_NONE = 0, - TOOLBAR_LEFT, - TOOLBAR_RIGHT, - TOOLBAR_BOTTOM, - - TOOLBAR_COUNT, - - TOOLBAR_FIRST = TOOLBAR_LEFT, - TOOLBAR_LAST = TOOLBAR_BOTTOM, - }; - - LLView::EOrientation getOrientation(SideType sideType); -} - -// NOTE: This needs to occur before Param block declaration for proper compilation. -namespace LLInitParam -{ - template<> - struct TypeValues : public TypeValuesHelper - { - static void declareValues(); - }; - - template<> - struct TypeValues : public TypeValuesHelper - { - static void declareValues(); - }; -} - - -class LLToolBar -: public LLUICtrl -{ - friend class LLToolBarButton; -public: - - class LLCenterLayoutPanel : public LLLayoutPanel - { - public: - typedef boost::function reshape_callback_t; - - virtual ~LLCenterLayoutPanel() {} - /*virtual*/ void handleReshape(const LLRect& rect, bool by_user); - - void setLocationId(LLToolBarEnums::EToolBarLocation id) { mLocationId = id; } - void setReshapeCallback(reshape_callback_t cb) { mReshapeCallback = cb; } - void setButtonPanel(LLPanel * panel) { mButtonPanel = panel; } - - protected: - friend class LLUICtrlFactory; - LLCenterLayoutPanel(const Params& params) : LLLayoutPanel(params), mButtonPanel(NULL) {} - - private: - reshape_callback_t mReshapeCallback; - LLToolBarEnums::EToolBarLocation mLocationId; - LLPanel * mButtonPanel; - }; - - struct Params : public LLInitParam::Block - { - Mandatory button_display_mode; - Mandatory side; - - Optional button_icon, - button_icon_and_text; - - Optional read_only, - wrap; - - Optional pad_left, - pad_top, - pad_right, - pad_bottom, - pad_between, - min_girth; - - // default command set - Multiple commands; - - Optional button_panel; - - Params(); - }; - - // virtuals - void draw(); - void reshape(S32 width, S32 height, bool called_from_parent = true); - bool handleRightMouseDown(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); - - static const int RANK_NONE = -1; - bool addCommand(const LLCommandId& commandId, int rank = RANK_NONE); - int removeCommand(const LLCommandId& commandId); // Returns the rank the removed command was at, RANK_NONE if not found - bool hasCommand(const LLCommandId& commandId) const; // is this command bound to a button in this toolbar - bool enableCommand(const LLCommandId& commandId, bool enabled); // enable/disable button bound to the specified command, if it exists in this toolbar - bool stopCommandInProgress(const LLCommandId& commandId); // stop command if it is currently active - bool flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing = false); // flash button associated with given command, if in this toolbar - - void setStartDragCallback(tool_startdrag_callback_t cb) { mStartDragItemCallback = cb; } // connects drag and drop behavior to external logic - void setHandleDragCallback(tool_handledrag_callback_t cb) { mHandleDragItemCallback = cb; } - void setHandleDropCallback(tool_handledrop_callback_t cb) { mHandleDropCallback = cb; } - bool isReadOnly() const { return mReadOnly; } - LLCenterLayoutPanel * getCenterLayoutPanel() const { return mCenterPanel; } - - LLToolBarButton* createButton(const LLCommandId& id); - - typedef boost::signals2::signal button_signal_t; - boost::signals2::connection setButtonAddCallback(const button_signal_t::slot_type& cb); - boost::signals2::connection setButtonEnterCallback(const button_signal_t::slot_type& cb); - boost::signals2::connection setButtonLeaveCallback(const button_signal_t::slot_type& cb); - boost::signals2::connection setButtonRemoveCallback(const button_signal_t::slot_type& cb); - - // append the specified string to end of tooltip - void setTooltipButtonSuffix(const std::string& suffix) { mButtonTooltipSuffix = suffix; } - - LLToolBarEnums::SideType getSideType() const { return mSideType; } - bool hasButtons() const { return !mButtons.empty(); } - bool isModified() const { return mModified; } - - int getRankFromPosition(S32 x, S32 y); - int getRankFromPosition(const LLCommandId& id); - - // Methods used in loading and saving toolbar settings - void setButtonType(LLToolBarEnums::ButtonType button_type); - LLToolBarEnums::ButtonType getButtonType() { return mButtonType; } - command_id_list_t& getCommandsList() { return mButtonCommands; } - void clearCommandsList(); - -private: - friend class LLUICtrlFactory; - LLToolBar(const Params&); - ~LLToolBar(); - - void initFromParams(const Params&); - void createContextMenu(); - void updateLayoutAsNeeded(); - void createButtons(); - void resizeButtonsInRow(std::vector& buttons_in_row, S32 max_row_girth); - bool isSettingChecked(const LLSD& userdata); - void onSettingEnable(const LLSD& userdata); - void onRemoveSelectedCommand(); - -private: - // static layout state - const bool mReadOnly; - const LLToolBarEnums::SideType mSideType; - const bool mWrap; - const S32 mPadLeft, - mPadRight, - mPadTop, - mPadBottom, - mPadBetween, - mMinGirth; - - // drag and drop state - tool_startdrag_callback_t mStartDragItemCallback; - tool_handledrag_callback_t mHandleDragItemCallback; - tool_handledrop_callback_t mHandleDropCallback; - bool mDragAndDropTarget; - int mDragRank; - S32 mDragx, - mDragy, - mDragGirth; - - typedef std::list toolbar_button_list; - typedef std::map command_id_map; - toolbar_button_list mButtons; - command_id_list_t mButtonCommands; - command_id_map mButtonMap; - - LLToolBarEnums::ButtonType mButtonType; - LLToolBarButton::Params mButtonParams[LLToolBarEnums::BTNTYPE_COUNT]; - - // related widgets - LLLayoutStack* mCenteringStack; - LLCenterLayoutPanel* mCenterPanel; - LLPanel* mButtonPanel; - LLHandle mPopupMenuHandle; - LLHandle mRemoveButtonHandle; - - LLToolBarButton* mRightMouseTargetButton; - - bool mNeedsLayout; - bool mModified; - - button_signal_t* mButtonAddSignal; - button_signal_t* mButtonEnterSignal; - button_signal_t* mButtonLeaveSignal; - button_signal_t* mButtonRemoveSignal; - - std::string mButtonTooltipSuffix; - - LLIconCtrl* mCaretIcon; -}; - - -#endif // LL_LLTOOLBAR_H +/** + * @file lltoolbar.h + * @author Richard Nelson + * @brief User customizable toolbar class + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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_LLTOOLBAR_H +#define LL_LLTOOLBAR_H + +#include "llbutton.h" +#include "llcommandmanager.h" +#include "lllayoutstack.h" +#include "lluictrl.h" +#include "llcommandmanager.h" +#include "llassettype.h" + +class LLToolBar; +class LLToolBarButton; +class LLIconCtrl; + +typedef boost::function tool_startdrag_callback_t; +typedef boost::function tool_handledrag_callback_t; +typedef boost::function tool_handledrop_callback_t; + +class LLToolBarButton : public LLButton +{ + friend class LLToolBar; +public: + struct Params : public LLInitParam::Block + { + Optional button_width; + Optional desired_height; + + Params() + : button_width("button_width"), + desired_height("desired_height", 20) + {} + + }; + + LLToolBarButton(const Params& p); + ~LLToolBarButton(); + + bool handleMouseDown(S32 x, S32 y, MASK mask); + bool handleHover(S32 x, S32 y, MASK mask); + + void reshape(S32 width, S32 height, bool called_from_parent = true); + void setEnabled(bool enabled); + void setCommandId(const LLCommandId& id) { mId = id; } + LLCommandId getCommandId() { return mId; } + + void setStartDragCallback(tool_startdrag_callback_t cb) { mStartDragItemCallback = cb; } + void setHandleDragCallback(tool_handledrag_callback_t cb) { mHandleDragItemCallback = cb; } + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + void onMouseCaptureLost(); + + void onCommit(); + + virtual const std::string getToolTip() const; + +private: + void callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param ); + + LLCommandId mId; + S32 mMouseDownX; + S32 mMouseDownY; + LLUI::RangeS32 mWidthRange; + S32 mDesiredHeight; + bool mIsDragged; + tool_startdrag_callback_t mStartDragItemCallback; + tool_handledrag_callback_t mHandleDragItemCallback; + + enable_signal_t* mIsEnabledSignal; + enable_signal_t* mIsRunningSignal; + enable_signal_t* mIsStartingSignal; + LLPointer mOriginalImageSelected, + mOriginalImageUnselected, + mOriginalImagePressed, + mOriginalImagePressedSelected; + LLUIColor mOriginalLabelColor, + mOriginalLabelColorSelected, + mOriginalImageOverlayColor, + mOriginalImageOverlaySelectedColor; +}; + + +namespace LLToolBarEnums +{ + enum ButtonType + { + BTNTYPE_ICONS_WITH_TEXT = 0, + BTNTYPE_ICONS_ONLY, + + BTNTYPE_COUNT + }; + + enum SideType + { + SIDE_BOTTOM, + SIDE_LEFT, + SIDE_RIGHT, + SIDE_TOP, + }; + + enum EToolBarLocation + { + TOOLBAR_NONE = 0, + TOOLBAR_LEFT, + TOOLBAR_RIGHT, + TOOLBAR_BOTTOM, + + TOOLBAR_COUNT, + + TOOLBAR_FIRST = TOOLBAR_LEFT, + TOOLBAR_LAST = TOOLBAR_BOTTOM, + }; + + LLView::EOrientation getOrientation(SideType sideType); +} + +// NOTE: This needs to occur before Param block declaration for proper compilation. +namespace LLInitParam +{ + template<> + struct TypeValues : public TypeValuesHelper + { + static void declareValues(); + }; + + template<> + struct TypeValues : public TypeValuesHelper + { + static void declareValues(); + }; +} + + +class LLToolBar +: public LLUICtrl +{ + friend class LLToolBarButton; +public: + + class LLCenterLayoutPanel : public LLLayoutPanel + { + public: + typedef boost::function reshape_callback_t; + + virtual ~LLCenterLayoutPanel() {} + /*virtual*/ void handleReshape(const LLRect& rect, bool by_user); + + void setLocationId(LLToolBarEnums::EToolBarLocation id) { mLocationId = id; } + void setReshapeCallback(reshape_callback_t cb) { mReshapeCallback = cb; } + void setButtonPanel(LLPanel * panel) { mButtonPanel = panel; } + + protected: + friend class LLUICtrlFactory; + LLCenterLayoutPanel(const Params& params) : LLLayoutPanel(params), mButtonPanel(NULL) {} + + private: + reshape_callback_t mReshapeCallback; + LLToolBarEnums::EToolBarLocation mLocationId; + LLPanel * mButtonPanel; + }; + + struct Params : public LLInitParam::Block + { + Mandatory button_display_mode; + Mandatory side; + + Optional button_icon, + button_icon_and_text; + + Optional read_only, + wrap; + + Optional pad_left, + pad_top, + pad_right, + pad_bottom, + pad_between, + min_girth; + + // default command set + Multiple commands; + + Optional button_panel; + + Params(); + }; + + // virtuals + void draw(); + void reshape(S32 width, S32 height, bool called_from_parent = true); + bool handleRightMouseDown(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); + + static const int RANK_NONE = -1; + bool addCommand(const LLCommandId& commandId, int rank = RANK_NONE); + int removeCommand(const LLCommandId& commandId); // Returns the rank the removed command was at, RANK_NONE if not found + bool hasCommand(const LLCommandId& commandId) const; // is this command bound to a button in this toolbar + bool enableCommand(const LLCommandId& commandId, bool enabled); // enable/disable button bound to the specified command, if it exists in this toolbar + bool stopCommandInProgress(const LLCommandId& commandId); // stop command if it is currently active + bool flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing = false); // flash button associated with given command, if in this toolbar + + void setStartDragCallback(tool_startdrag_callback_t cb) { mStartDragItemCallback = cb; } // connects drag and drop behavior to external logic + void setHandleDragCallback(tool_handledrag_callback_t cb) { mHandleDragItemCallback = cb; } + void setHandleDropCallback(tool_handledrop_callback_t cb) { mHandleDropCallback = cb; } + bool isReadOnly() const { return mReadOnly; } + LLCenterLayoutPanel * getCenterLayoutPanel() const { return mCenterPanel; } + + LLToolBarButton* createButton(const LLCommandId& id); + + typedef boost::signals2::signal button_signal_t; + boost::signals2::connection setButtonAddCallback(const button_signal_t::slot_type& cb); + boost::signals2::connection setButtonEnterCallback(const button_signal_t::slot_type& cb); + boost::signals2::connection setButtonLeaveCallback(const button_signal_t::slot_type& cb); + boost::signals2::connection setButtonRemoveCallback(const button_signal_t::slot_type& cb); + + // append the specified string to end of tooltip + void setTooltipButtonSuffix(const std::string& suffix) { mButtonTooltipSuffix = suffix; } + + LLToolBarEnums::SideType getSideType() const { return mSideType; } + bool hasButtons() const { return !mButtons.empty(); } + bool isModified() const { return mModified; } + + int getRankFromPosition(S32 x, S32 y); + int getRankFromPosition(const LLCommandId& id); + + // Methods used in loading and saving toolbar settings + void setButtonType(LLToolBarEnums::ButtonType button_type); + LLToolBarEnums::ButtonType getButtonType() { return mButtonType; } + command_id_list_t& getCommandsList() { return mButtonCommands; } + void clearCommandsList(); + +private: + friend class LLUICtrlFactory; + LLToolBar(const Params&); + ~LLToolBar(); + + void initFromParams(const Params&); + void createContextMenu(); + void updateLayoutAsNeeded(); + void createButtons(); + void resizeButtonsInRow(std::vector& buttons_in_row, S32 max_row_girth); + bool isSettingChecked(const LLSD& userdata); + void onSettingEnable(const LLSD& userdata); + void onRemoveSelectedCommand(); + +private: + // static layout state + const bool mReadOnly; + const LLToolBarEnums::SideType mSideType; + const bool mWrap; + const S32 mPadLeft, + mPadRight, + mPadTop, + mPadBottom, + mPadBetween, + mMinGirth; + + // drag and drop state + tool_startdrag_callback_t mStartDragItemCallback; + tool_handledrag_callback_t mHandleDragItemCallback; + tool_handledrop_callback_t mHandleDropCallback; + bool mDragAndDropTarget; + int mDragRank; + S32 mDragx, + mDragy, + mDragGirth; + + typedef std::list toolbar_button_list; + typedef std::map command_id_map; + toolbar_button_list mButtons; + command_id_list_t mButtonCommands; + command_id_map mButtonMap; + + LLToolBarEnums::ButtonType mButtonType; + LLToolBarButton::Params mButtonParams[LLToolBarEnums::BTNTYPE_COUNT]; + + // related widgets + LLLayoutStack* mCenteringStack; + LLCenterLayoutPanel* mCenterPanel; + LLPanel* mButtonPanel; + LLHandle mPopupMenuHandle; + LLHandle mRemoveButtonHandle; + + LLToolBarButton* mRightMouseTargetButton; + + bool mNeedsLayout; + bool mModified; + + button_signal_t* mButtonAddSignal; + button_signal_t* mButtonEnterSignal; + button_signal_t* mButtonLeaveSignal; + button_signal_t* mButtonRemoveSignal; + + std::string mButtonTooltipSuffix; + + LLIconCtrl* mCaretIcon; +}; + + +#endif // LL_LLTOOLBAR_H diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 5664a1a153..86525c2f7e 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -1,648 +1,648 @@ -/** - * @file lltooltip.cpp - * @brief LLToolTipMgr class implementation and related classes - * - * $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 "linden_common.h" - -// self include -#include "lltooltip.h" - -// Library includes -#include "lltextbox.h" -#include "lliconctrl.h" -#include "llbutton.h" -#include "llmenugl.h" // hideMenus() -#include "llui.h" // positionViewNearMouse() -#include "llwindow.h" -#include "lltrans.h" -// -// Constants -// - -// -// Local globals -// - -LLToolTipView *gToolTipView = NULL; - -// -// Member functions -// - -static LLDefaultChildRegistry::Register register_tooltip_view("tooltip_view"); - -LLToolTipView::Params::Params() -{ - changeDefault(mouse_opaque, false); -} - -LLToolTipView::LLToolTipView(const LLToolTipView::Params& p) -: LLView(p) -{ -} - -void LLToolTipView::draw() -{ - LLToolTipMgr::instance().updateToolTipVisibility(); - - // do the usual thing - LLView::draw(); -} - -bool LLToolTipView::handleHover(S32 x, S32 y, MASK mask) -{ - static S32 last_x = x; - static S32 last_y = y; - - LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance(); - - if (x != last_x && y != last_y && !tooltip_mgr.getMouseNearRect().pointInRect(x, y)) - { - // allow new tooltips because mouse moved outside of mouse near rect - tooltip_mgr.unblockToolTips(); - } - - last_x = x; - last_y = y; - return LLView::handleHover(x, y, mask); -} - -bool LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask) -{ - LLToolTipMgr::instance().blockToolTips(); - - if (LLView::handleMouseDown(x, y, mask)) - { - // If we are handling the mouse event menu holder - // won't get a chance to close menus so do this here - LLMenuGL::sMenuContainer->hideMenus(); - return true; - } - - return false; -} - -bool LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - LLToolTipMgr::instance().blockToolTips(); - return LLView::handleMiddleMouseDown(x, y, mask); -} - -bool LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - LLToolTipMgr::instance().blockToolTips(); - return LLView::handleRightMouseDown(x, y, mask); -} - - -bool LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks ) -{ - LLToolTipMgr::instance().blockToolTips(); - return false; -} - -void LLToolTipView::drawStickyRect() -{ - gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false); -} - -// defaults for floater param block pulled from widgets/floater.xml -static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(&typeid(LLInspector::Params), "inspector"); - -// -// LLToolTip -// - - -static LLDefaultChildRegistry::Register register_tooltip("tool_tip"); - - -LLToolTip::Params::Params() -: max_width("max_width", 200), - padding("padding", 4), - wrap("wrap", true), - pos("pos"), - message("message"), - delay_time("delay_time", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipDelay" )), - visible_time_over("visible_time_over", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )), - visible_time_near("visible_time_near", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )), - visible_time_far("visible_time_far", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )), - sticky_rect("sticky_rect"), - image("image"), - text_color("text_color"), - time_based_media("time_based_media", false), - web_based_media("web_based_media", false), - media_playing("media_playing", false), - allow_paste_tooltip("allow_paste_tooltip", false) -{ - changeDefault(chrome, true); -} - -LLToolTip::LLToolTip(const LLToolTip::Params& p) -: LLPanel(p), - mHasClickCallback(p.click_callback.isProvided()), - mPadding(p.padding), - mMaxWidth(p.max_width), - mTextBox(NULL), - mInfoButton(NULL), - mPlayMediaButton(NULL), - mHomePageButton(NULL), - mIsTooltipPastable(p.allow_paste_tooltip) -{ - LLTextBox::Params params; - params.name = params.initial_value().asString(); - // bake textbox padding into initial rect - params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding); - params.h_pad = 0; - params.v_pad = 0; - params.mouse_opaque = false; - params.text_color = p.text_color; - params.bg_visible = false; - params.font = p.font; - params.use_ellipses = true; - params.wrap = p.wrap; - params.font_valign = LLFontGL::VCENTER; - params.parse_urls = false; // disallow hyperlinks in tooltips, as they want to spawn their own explanatory tooltips - mTextBox = LLUICtrlFactory::create (params); - addChild(mTextBox); - - S32 TOOLTIP_ICON_SIZE = 0; - S32 TOOLTIP_PLAYBUTTON_SIZE = 0; - if (p.image.isProvided()) - { - LLButton::Params icon_params; - icon_params.name = "tooltip_info"; - LLRect icon_rect; - LLUIImage* imagep = p.image; - TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16); - icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); - icon_params.rect = icon_rect; - icon_params.image_unselected(imagep); - icon_params.image_selected(imagep); - - icon_params.scale_image(true); - icon_params.flash_color.control = "ButtonUnselectedFgColor"; - mInfoButton = LLUICtrlFactory::create(icon_params); - if (p.click_callback.isProvided()) - { - mInfoButton->setCommitCallback(boost::bind(p.click_callback())); - } - addChild(mInfoButton); - - // move text over to fit image in - mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0); - } - - if (p.time_based_media) - { - LLButton::Params p_button; - p_button.name(std::string("play_media")); - p_button.label(""); // provide label but set to empty so name does not overwrite it -angela - TOOLTIP_PLAYBUTTON_SIZE = 16; - LLRect button_rect; - button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); - p_button.rect = button_rect; - p_button.image_selected.name("button_anim_pause.tga"); - p_button.image_unselected.name("button_anim_play.tga"); - p_button.scale_image(true); - - mPlayMediaButton = LLUICtrlFactory::create(p_button); - if(p.click_playmedia_callback.isProvided()) - { - mPlayMediaButton->setCommitCallback(boost::bind(p.click_playmedia_callback())); - } - mPlayMediaButton->setToggleState(p.media_playing); - addChild(mPlayMediaButton); - - // move text over to fit image in - mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0); - } - - if (p.web_based_media) - { - LLButton::Params p_w_button; - p_w_button.name(std::string("home_page")); - p_w_button.label(""); // provid label but set to empty so name does not overwrite it -angela - TOOLTIP_PLAYBUTTON_SIZE = 16; - LLRect button_rect; - button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); - p_w_button.rect = button_rect; - p_w_button.image_unselected.name("map_home.tga"); - p_w_button.scale_image(true); - - mHomePageButton = LLUICtrlFactory::create(p_w_button); - if(p.click_homepage_callback.isProvided()) - { - mHomePageButton->setCommitCallback(boost::bind(p.click_homepage_callback())); - } - addChild(mHomePageButton); - - // move text over to fit image in - mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0); - } - - if (p.click_callback.isProvided()) - { - setMouseUpCallback(boost::bind(p.click_callback())); - } -} - -void LLToolTip::initFromParams(const LLToolTip::Params& p) -{ - LLPanel::initFromParams(p); - - // do this *after* we've had our size set in LLPanel::initFromParams(); - const S32 REALLY_LARGE_HEIGHT = 10000; - mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT); - - if (p.styled_message.isProvided()) - { - for (LLInitParam::ParamIterator::const_iterator text_it = p.styled_message.begin(); - text_it != p.styled_message.end(); - ++text_it) - { - mTextBox->appendText(text_it->text(), false, text_it->style); - } - } - else - { - mTextBox->setText(p.message()); - } - - mIsTooltipPastable = p.allow_paste_tooltip; - - updateTextBox(); - snapToChildren(); -} - -void LLToolTip::updateTextBox() -{ - S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1); - S32 text_height = mTextBox->getTextPixelHeight(); - mTextBox->reshape(text_width, text_height); -} - -void LLToolTip::snapToChildren() -{ - // reshape tooltip panel to fit text box - LLRect tooltip_rect = calcBoundingRect(); - tooltip_rect.mTop += mPadding; - tooltip_rect.mRight += mPadding; - tooltip_rect.mBottom = 0; - tooltip_rect.mLeft = 0; - - if (mInfoButton) - { - mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding)); - - LLRect text_rect = mTextBox->getRect(); - LLRect icon_rect = mInfoButton->getRect(); - mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY()); - } - - setShape(tooltip_rect); -} - -void LLToolTip::setVisible(bool visible) -{ - // fade out tooltip over time - if (visible) - { - mVisibleTimer.start(); - mFadeTimer.stop(); - LLPanel::setVisible(true); - } - else - { - mVisibleTimer.stop(); - // don't actually change mVisible state, start fade out transition instead - if (!mFadeTimer.getStarted()) - { - mFadeTimer.start(); - } - } -} - -bool LLToolTip::handleHover(S32 x, S32 y, MASK mask) -{ - //mInfoButton->setFlashing(true); - if(mInfoButton) - mInfoButton->setHighlight(true); - - LLPanel::handleHover(x, y, mask); - if (mHasClickCallback) - { - getWindow()->setCursor(UI_CURSOR_HAND); - } - return true; -} - -void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask) -{ - //mInfoButton->setFlashing(true); - if(mInfoButton) - mInfoButton->setHighlight(false); - LLUICtrl::onMouseLeave(x, y, mask); -} - -void LLToolTip::draw() -{ - F32 alpha = 1.f; - - if (mFadeTimer.getStarted()) - { - static LLCachedControl tool_tip_fade_time(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFadeTime", 0.2f); - alpha = clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, tool_tip_fade_time(), 1.f, 0.f); - if (alpha == 0.f) - { - // finished fading out, so hide ourselves - mFadeTimer.stop(); - LLPanel::setVisible(false); - } - } - - // draw tooltip contents with appropriate alpha - { - LLViewDrawContext context(alpha); - LLPanel::draw(); - } -} - -bool LLToolTip::isFading() -{ - return mFadeTimer.getStarted(); -} - -F32 LLToolTip::getVisibleTime() -{ - return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f; -} - -bool LLToolTip::hasClickCallback() -{ - return mHasClickCallback; -} - -void LLToolTip::getToolTipMessage(std::string & message) -{ - if (mTextBox) - { - message = mTextBox->getText(); - } -} - - - -// -// LLToolTipMgr -// - -LLToolTipMgr::LLToolTipMgr() -: mToolTipsBlocked(false), - mToolTip(NULL), - mNeedsToolTip(false) -{} - -void LLToolTipMgr::createToolTip(const LLToolTip::Params& params) -{ - // block all other tooltips until tooltips re-enabled (e.g. mouse moved) - blockToolTips(); - - delete mToolTip; - - LLToolTip::Params tooltip_params(params); - // block mouse events if there is a click handler registered (specifically, hover) - if (params.click_callback.isProvided() && !params.mouse_opaque.isProvided()) - { - // set mouse_opaque to true if it wasn't already set to something else - // this prevents mouse down from going "through" the tooltip and ultimately - // causing the tooltip to disappear - tooltip_params.mouse_opaque = true; - } - tooltip_params.rect = LLRect (0, 1, 1, 0); - - if (tooltip_params.create_callback.isProvided()) - { - mToolTip = tooltip_params.create_callback()(tooltip_params); - if (mToolTip == NULL) - { - return; - } - } - else - mToolTip = LLUICtrlFactory::create (tooltip_params); - - gToolTipView->addChild(mToolTip); - - if (params.pos.isProvided()) - { - LLCoordGL pos = params.pos; - // try to spawn at requested position - LLUI::getInstance()->positionViewNearMouse(mToolTip, pos.mX, pos.mY); - } - else - { - // just spawn at mouse location - LLUI::getInstance()->positionViewNearMouse(mToolTip); - } - - //...update "sticky" rect and tooltip position - if (params.sticky_rect.isProvided()) - { - mMouseNearRect = params.sticky_rect; - } - else - { - S32 mouse_x; - S32 mouse_y; - LLUI::getInstance()->getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y); - - // allow mouse a little bit of slop before changing tooltips - mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3); - } - - // allow mouse to move all the way to the tooltip without changing tooltips - // (tooltip can still time out) - if (mToolTip->hasClickCallback()) - { - // keep tooltip up when we mouse over it - mMouseNearRect.unionWith(mToolTip->getRect()); - } -} - - -void LLToolTipMgr::show(const std::string& msg, bool allow_paste_tooltip) -{ - show(LLToolTip::Params().message(msg).allow_paste_tooltip(allow_paste_tooltip)); -} - -void LLToolTipMgr::show(const LLToolTip::Params& params) -{ - if (!params.styled_message.isProvided() - && (!params.message.isProvided() || params.message().empty()) - && !params.image.isProvided() && !params.create_callback.isProvided()) return; - - // fill in default tooltip params from tool_tip.xml - LLToolTip::Params params_with_defaults(params); - params_with_defaults.fillFrom(LLUICtrlFactory::instance().getDefaultParams()); - if (!params_with_defaults.validateBlock()) - { - LL_WARNS() << "Could not display tooltip!" << LL_ENDL; - return; - } - - // are we ready to show the tooltip? - if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc. - && LLUI::getInstance()->getMouseIdleTime() > params_with_defaults.delay_time) // the mouse has been still long enough - { - bool tooltip_changed = mLastToolTipParams.message() != params_with_defaults.message() - || mLastToolTipParams.pos() != params_with_defaults.pos() - || mLastToolTipParams.time_based_media() != params_with_defaults.time_based_media() - || mLastToolTipParams.web_based_media() != params_with_defaults.web_based_media(); - - bool tooltip_shown = mToolTip - && mToolTip->getVisible() - && !mToolTip->isFading(); - - mNeedsToolTip = tooltip_changed || !tooltip_shown; - // store description of tooltip for later creation - mNextToolTipParams = params_with_defaults; - } -} - -// allow new tooltips to be created, e.g. after mouse has moved -void LLToolTipMgr::unblockToolTips() -{ - mToolTipsBlocked = false; -} - -// disallow new tooltips until unblockTooltips called -void LLToolTipMgr::blockToolTips() -{ - hideToolTips(); - mToolTipsBlocked = true; -} - -void LLToolTipMgr::hideToolTips() -{ - if (mToolTip) - { - mToolTip->setVisible(false); - } -} - -bool LLToolTipMgr::toolTipVisible() -{ - return mToolTip ? mToolTip->isInVisibleChain() : false; -} - -LLRect LLToolTipMgr::getToolTipRect() -{ - if (mToolTip && mToolTip->getVisible()) - { - return mToolTip->getRect(); - } - return LLRect(); -} - - -LLRect LLToolTipMgr::getMouseNearRect() -{ - return toolTipVisible() ? mMouseNearRect : LLRect(); -} - -// every frame, determine if current tooltip should be hidden -void LLToolTipMgr::updateToolTipVisibility() -{ - // create new tooltip if we have one ready to go - if (mNeedsToolTip) - { - mNeedsToolTip = false; - createToolTip(mNextToolTipParams); - mLastToolTipParams = mNextToolTipParams; - - return; - } - - // hide tooltips when mouse cursor is hidden - if (LLUI::getInstance()->getWindow()->isCursorHidden()) - { - blockToolTips(); - return; - } - - // hide existing tooltips if they have timed out - F32 tooltip_timeout = 0.f; - if (toolTipVisible()) - { - S32 mouse_x, mouse_y; - LLUI::getInstance()->getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y); - - // mouse far away from tooltip - tooltip_timeout = mLastToolTipParams.visible_time_far; - // mouse near rect will only include the tooltip if the - // tooltip is clickable - if (mMouseNearRect.pointInRect(mouse_x, mouse_y)) - { - // mouse "close" to tooltip - tooltip_timeout = mLastToolTipParams.visible_time_near; - - // if tooltip is clickable (has large mMouseNearRect) - // than having cursor over tooltip keeps it up indefinitely - if (mToolTip->parentPointInView(mouse_x, mouse_y)) - { - // mouse over tooltip itself, don't time out - tooltip_timeout = mLastToolTipParams.visible_time_over; - } - } - - if (mToolTip->getVisibleTime() > tooltip_timeout) - { - hideToolTips(); - unblockToolTips(); - } - } -} - - -// Return the current tooltip text -void LLToolTipMgr::getToolTipMessage(std::string & message) -{ - if (toolTipVisible()) - { - mToolTip->getToolTipMessage(message); - } -} - -bool LLToolTipMgr::isTooltipPastable() -{ - if (toolTipVisible()) - { - return mToolTip->isTooltipPastable(); - } - return false; - } - -// EOF +/** + * @file lltooltip.cpp + * @brief LLToolTipMgr class implementation and related classes + * + * $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 "linden_common.h" + +// self include +#include "lltooltip.h" + +// Library includes +#include "lltextbox.h" +#include "lliconctrl.h" +#include "llbutton.h" +#include "llmenugl.h" // hideMenus() +#include "llui.h" // positionViewNearMouse() +#include "llwindow.h" +#include "lltrans.h" +// +// Constants +// + +// +// Local globals +// + +LLToolTipView *gToolTipView = NULL; + +// +// Member functions +// + +static LLDefaultChildRegistry::Register register_tooltip_view("tooltip_view"); + +LLToolTipView::Params::Params() +{ + changeDefault(mouse_opaque, false); +} + +LLToolTipView::LLToolTipView(const LLToolTipView::Params& p) +: LLView(p) +{ +} + +void LLToolTipView::draw() +{ + LLToolTipMgr::instance().updateToolTipVisibility(); + + // do the usual thing + LLView::draw(); +} + +bool LLToolTipView::handleHover(S32 x, S32 y, MASK mask) +{ + static S32 last_x = x; + static S32 last_y = y; + + LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance(); + + if (x != last_x && y != last_y && !tooltip_mgr.getMouseNearRect().pointInRect(x, y)) + { + // allow new tooltips because mouse moved outside of mouse near rect + tooltip_mgr.unblockToolTips(); + } + + last_x = x; + last_y = y; + return LLView::handleHover(x, y, mask); +} + +bool LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask) +{ + LLToolTipMgr::instance().blockToolTips(); + + if (LLView::handleMouseDown(x, y, mask)) + { + // If we are handling the mouse event menu holder + // won't get a chance to close menus so do this here + LLMenuGL::sMenuContainer->hideMenus(); + return true; + } + + return false; +} + +bool LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + LLToolTipMgr::instance().blockToolTips(); + return LLView::handleMiddleMouseDown(x, y, mask); +} + +bool LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLToolTipMgr::instance().blockToolTips(); + return LLView::handleRightMouseDown(x, y, mask); +} + + +bool LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks ) +{ + LLToolTipMgr::instance().blockToolTips(); + return false; +} + +void LLToolTipView::drawStickyRect() +{ + gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false); +} + +// defaults for floater param block pulled from widgets/floater.xml +static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(&typeid(LLInspector::Params), "inspector"); + +// +// LLToolTip +// + + +static LLDefaultChildRegistry::Register register_tooltip("tool_tip"); + + +LLToolTip::Params::Params() +: max_width("max_width", 200), + padding("padding", 4), + wrap("wrap", true), + pos("pos"), + message("message"), + delay_time("delay_time", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipDelay" )), + visible_time_over("visible_time_over", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )), + visible_time_near("visible_time_near", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )), + visible_time_far("visible_time_far", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )), + sticky_rect("sticky_rect"), + image("image"), + text_color("text_color"), + time_based_media("time_based_media", false), + web_based_media("web_based_media", false), + media_playing("media_playing", false), + allow_paste_tooltip("allow_paste_tooltip", false) +{ + changeDefault(chrome, true); +} + +LLToolTip::LLToolTip(const LLToolTip::Params& p) +: LLPanel(p), + mHasClickCallback(p.click_callback.isProvided()), + mPadding(p.padding), + mMaxWidth(p.max_width), + mTextBox(NULL), + mInfoButton(NULL), + mPlayMediaButton(NULL), + mHomePageButton(NULL), + mIsTooltipPastable(p.allow_paste_tooltip) +{ + LLTextBox::Params params; + params.name = params.initial_value().asString(); + // bake textbox padding into initial rect + params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding); + params.h_pad = 0; + params.v_pad = 0; + params.mouse_opaque = false; + params.text_color = p.text_color; + params.bg_visible = false; + params.font = p.font; + params.use_ellipses = true; + params.wrap = p.wrap; + params.font_valign = LLFontGL::VCENTER; + params.parse_urls = false; // disallow hyperlinks in tooltips, as they want to spawn their own explanatory tooltips + mTextBox = LLUICtrlFactory::create (params); + addChild(mTextBox); + + S32 TOOLTIP_ICON_SIZE = 0; + S32 TOOLTIP_PLAYBUTTON_SIZE = 0; + if (p.image.isProvided()) + { + LLButton::Params icon_params; + icon_params.name = "tooltip_info"; + LLRect icon_rect; + LLUIImage* imagep = p.image; + TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16); + icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); + icon_params.rect = icon_rect; + icon_params.image_unselected(imagep); + icon_params.image_selected(imagep); + + icon_params.scale_image(true); + icon_params.flash_color.control = "ButtonUnselectedFgColor"; + mInfoButton = LLUICtrlFactory::create(icon_params); + if (p.click_callback.isProvided()) + { + mInfoButton->setCommitCallback(boost::bind(p.click_callback())); + } + addChild(mInfoButton); + + // move text over to fit image in + mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0); + } + + if (p.time_based_media) + { + LLButton::Params p_button; + p_button.name(std::string("play_media")); + p_button.label(""); // provide label but set to empty so name does not overwrite it -angela + TOOLTIP_PLAYBUTTON_SIZE = 16; + LLRect button_rect; + button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); + p_button.rect = button_rect; + p_button.image_selected.name("button_anim_pause.tga"); + p_button.image_unselected.name("button_anim_play.tga"); + p_button.scale_image(true); + + mPlayMediaButton = LLUICtrlFactory::create(p_button); + if(p.click_playmedia_callback.isProvided()) + { + mPlayMediaButton->setCommitCallback(boost::bind(p.click_playmedia_callback())); + } + mPlayMediaButton->setToggleState(p.media_playing); + addChild(mPlayMediaButton); + + // move text over to fit image in + mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0); + } + + if (p.web_based_media) + { + LLButton::Params p_w_button; + p_w_button.name(std::string("home_page")); + p_w_button.label(""); // provid label but set to empty so name does not overwrite it -angela + TOOLTIP_PLAYBUTTON_SIZE = 16; + LLRect button_rect; + button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); + p_w_button.rect = button_rect; + p_w_button.image_unselected.name("map_home.tga"); + p_w_button.scale_image(true); + + mHomePageButton = LLUICtrlFactory::create(p_w_button); + if(p.click_homepage_callback.isProvided()) + { + mHomePageButton->setCommitCallback(boost::bind(p.click_homepage_callback())); + } + addChild(mHomePageButton); + + // move text over to fit image in + mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0); + } + + if (p.click_callback.isProvided()) + { + setMouseUpCallback(boost::bind(p.click_callback())); + } +} + +void LLToolTip::initFromParams(const LLToolTip::Params& p) +{ + LLPanel::initFromParams(p); + + // do this *after* we've had our size set in LLPanel::initFromParams(); + const S32 REALLY_LARGE_HEIGHT = 10000; + mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT); + + if (p.styled_message.isProvided()) + { + for (LLInitParam::ParamIterator::const_iterator text_it = p.styled_message.begin(); + text_it != p.styled_message.end(); + ++text_it) + { + mTextBox->appendText(text_it->text(), false, text_it->style); + } + } + else + { + mTextBox->setText(p.message()); + } + + mIsTooltipPastable = p.allow_paste_tooltip; + + updateTextBox(); + snapToChildren(); +} + +void LLToolTip::updateTextBox() +{ + S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1); + S32 text_height = mTextBox->getTextPixelHeight(); + mTextBox->reshape(text_width, text_height); +} + +void LLToolTip::snapToChildren() +{ + // reshape tooltip panel to fit text box + LLRect tooltip_rect = calcBoundingRect(); + tooltip_rect.mTop += mPadding; + tooltip_rect.mRight += mPadding; + tooltip_rect.mBottom = 0; + tooltip_rect.mLeft = 0; + + if (mInfoButton) + { + mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding)); + + LLRect text_rect = mTextBox->getRect(); + LLRect icon_rect = mInfoButton->getRect(); + mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY()); + } + + setShape(tooltip_rect); +} + +void LLToolTip::setVisible(bool visible) +{ + // fade out tooltip over time + if (visible) + { + mVisibleTimer.start(); + mFadeTimer.stop(); + LLPanel::setVisible(true); + } + else + { + mVisibleTimer.stop(); + // don't actually change mVisible state, start fade out transition instead + if (!mFadeTimer.getStarted()) + { + mFadeTimer.start(); + } + } +} + +bool LLToolTip::handleHover(S32 x, S32 y, MASK mask) +{ + //mInfoButton->setFlashing(true); + if(mInfoButton) + mInfoButton->setHighlight(true); + + LLPanel::handleHover(x, y, mask); + if (mHasClickCallback) + { + getWindow()->setCursor(UI_CURSOR_HAND); + } + return true; +} + +void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask) +{ + //mInfoButton->setFlashing(true); + if(mInfoButton) + mInfoButton->setHighlight(false); + LLUICtrl::onMouseLeave(x, y, mask); +} + +void LLToolTip::draw() +{ + F32 alpha = 1.f; + + if (mFadeTimer.getStarted()) + { + static LLCachedControl tool_tip_fade_time(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFadeTime", 0.2f); + alpha = clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, tool_tip_fade_time(), 1.f, 0.f); + if (alpha == 0.f) + { + // finished fading out, so hide ourselves + mFadeTimer.stop(); + LLPanel::setVisible(false); + } + } + + // draw tooltip contents with appropriate alpha + { + LLViewDrawContext context(alpha); + LLPanel::draw(); + } +} + +bool LLToolTip::isFading() +{ + return mFadeTimer.getStarted(); +} + +F32 LLToolTip::getVisibleTime() +{ + return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f; +} + +bool LLToolTip::hasClickCallback() +{ + return mHasClickCallback; +} + +void LLToolTip::getToolTipMessage(std::string & message) +{ + if (mTextBox) + { + message = mTextBox->getText(); + } +} + + + +// +// LLToolTipMgr +// + +LLToolTipMgr::LLToolTipMgr() +: mToolTipsBlocked(false), + mToolTip(NULL), + mNeedsToolTip(false) +{} + +void LLToolTipMgr::createToolTip(const LLToolTip::Params& params) +{ + // block all other tooltips until tooltips re-enabled (e.g. mouse moved) + blockToolTips(); + + delete mToolTip; + + LLToolTip::Params tooltip_params(params); + // block mouse events if there is a click handler registered (specifically, hover) + if (params.click_callback.isProvided() && !params.mouse_opaque.isProvided()) + { + // set mouse_opaque to true if it wasn't already set to something else + // this prevents mouse down from going "through" the tooltip and ultimately + // causing the tooltip to disappear + tooltip_params.mouse_opaque = true; + } + tooltip_params.rect = LLRect (0, 1, 1, 0); + + if (tooltip_params.create_callback.isProvided()) + { + mToolTip = tooltip_params.create_callback()(tooltip_params); + if (mToolTip == NULL) + { + return; + } + } + else + mToolTip = LLUICtrlFactory::create (tooltip_params); + + gToolTipView->addChild(mToolTip); + + if (params.pos.isProvided()) + { + LLCoordGL pos = params.pos; + // try to spawn at requested position + LLUI::getInstance()->positionViewNearMouse(mToolTip, pos.mX, pos.mY); + } + else + { + // just spawn at mouse location + LLUI::getInstance()->positionViewNearMouse(mToolTip); + } + + //...update "sticky" rect and tooltip position + if (params.sticky_rect.isProvided()) + { + mMouseNearRect = params.sticky_rect; + } + else + { + S32 mouse_x; + S32 mouse_y; + LLUI::getInstance()->getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y); + + // allow mouse a little bit of slop before changing tooltips + mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3); + } + + // allow mouse to move all the way to the tooltip without changing tooltips + // (tooltip can still time out) + if (mToolTip->hasClickCallback()) + { + // keep tooltip up when we mouse over it + mMouseNearRect.unionWith(mToolTip->getRect()); + } +} + + +void LLToolTipMgr::show(const std::string& msg, bool allow_paste_tooltip) +{ + show(LLToolTip::Params().message(msg).allow_paste_tooltip(allow_paste_tooltip)); +} + +void LLToolTipMgr::show(const LLToolTip::Params& params) +{ + if (!params.styled_message.isProvided() + && (!params.message.isProvided() || params.message().empty()) + && !params.image.isProvided() && !params.create_callback.isProvided()) return; + + // fill in default tooltip params from tool_tip.xml + LLToolTip::Params params_with_defaults(params); + params_with_defaults.fillFrom(LLUICtrlFactory::instance().getDefaultParams()); + if (!params_with_defaults.validateBlock()) + { + LL_WARNS() << "Could not display tooltip!" << LL_ENDL; + return; + } + + // are we ready to show the tooltip? + if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc. + && LLUI::getInstance()->getMouseIdleTime() > params_with_defaults.delay_time) // the mouse has been still long enough + { + bool tooltip_changed = mLastToolTipParams.message() != params_with_defaults.message() + || mLastToolTipParams.pos() != params_with_defaults.pos() + || mLastToolTipParams.time_based_media() != params_with_defaults.time_based_media() + || mLastToolTipParams.web_based_media() != params_with_defaults.web_based_media(); + + bool tooltip_shown = mToolTip + && mToolTip->getVisible() + && !mToolTip->isFading(); + + mNeedsToolTip = tooltip_changed || !tooltip_shown; + // store description of tooltip for later creation + mNextToolTipParams = params_with_defaults; + } +} + +// allow new tooltips to be created, e.g. after mouse has moved +void LLToolTipMgr::unblockToolTips() +{ + mToolTipsBlocked = false; +} + +// disallow new tooltips until unblockTooltips called +void LLToolTipMgr::blockToolTips() +{ + hideToolTips(); + mToolTipsBlocked = true; +} + +void LLToolTipMgr::hideToolTips() +{ + if (mToolTip) + { + mToolTip->setVisible(false); + } +} + +bool LLToolTipMgr::toolTipVisible() +{ + return mToolTip ? mToolTip->isInVisibleChain() : false; +} + +LLRect LLToolTipMgr::getToolTipRect() +{ + if (mToolTip && mToolTip->getVisible()) + { + return mToolTip->getRect(); + } + return LLRect(); +} + + +LLRect LLToolTipMgr::getMouseNearRect() +{ + return toolTipVisible() ? mMouseNearRect : LLRect(); +} + +// every frame, determine if current tooltip should be hidden +void LLToolTipMgr::updateToolTipVisibility() +{ + // create new tooltip if we have one ready to go + if (mNeedsToolTip) + { + mNeedsToolTip = false; + createToolTip(mNextToolTipParams); + mLastToolTipParams = mNextToolTipParams; + + return; + } + + // hide tooltips when mouse cursor is hidden + if (LLUI::getInstance()->getWindow()->isCursorHidden()) + { + blockToolTips(); + return; + } + + // hide existing tooltips if they have timed out + F32 tooltip_timeout = 0.f; + if (toolTipVisible()) + { + S32 mouse_x, mouse_y; + LLUI::getInstance()->getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y); + + // mouse far away from tooltip + tooltip_timeout = mLastToolTipParams.visible_time_far; + // mouse near rect will only include the tooltip if the + // tooltip is clickable + if (mMouseNearRect.pointInRect(mouse_x, mouse_y)) + { + // mouse "close" to tooltip + tooltip_timeout = mLastToolTipParams.visible_time_near; + + // if tooltip is clickable (has large mMouseNearRect) + // than having cursor over tooltip keeps it up indefinitely + if (mToolTip->parentPointInView(mouse_x, mouse_y)) + { + // mouse over tooltip itself, don't time out + tooltip_timeout = mLastToolTipParams.visible_time_over; + } + } + + if (mToolTip->getVisibleTime() > tooltip_timeout) + { + hideToolTips(); + unblockToolTips(); + } + } +} + + +// Return the current tooltip text +void LLToolTipMgr::getToolTipMessage(std::string & message) +{ + if (toolTipVisible()) + { + mToolTip->getToolTipMessage(message); + } +} + +bool LLToolTipMgr::isTooltipPastable() +{ + if (toolTipVisible()) + { + return mToolTip->isTooltipPastable(); + } + return false; + } + +// EOF diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h index 74ae5a219d..8515504e3b 100644 --- a/indra/llui/lltooltip.h +++ b/indra/llui/lltooltip.h @@ -1,185 +1,185 @@ -/** - * @file lltooltip.h - * @brief LLToolTipMgr class definition and related classes - * - * $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$ - */ - -#ifndef LL_LLTOOLTIP_H -#define LL_LLTOOLTIP_H - -// Library includes -#include "llsingleton.h" -#include "llinitparam.h" -#include "llpanel.h" -#include "llstyle.h" - -// -// Classes -// -class LLToolTipView : public LLView -{ -public: - struct Params : public LLInitParam::Block - { - Params(); - }; - LLToolTipView(const LLToolTipView::Params&); - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks ); - - void drawStickyRect(); - - /*virtual*/ void draw(); -}; - -class LLToolTip : public LLPanel -{ -public: - - struct StyledText : public LLInitParam::Block - { - Mandatory text; - Optional style; - }; - - struct Params : public LLInitParam::Block - { - typedef boost::function click_callback_t; - typedef boost::function create_callback_t; - - Optional message; - Multiple styled_message; - - Optional pos; - Optional delay_time, - visible_time_over, // time for which tooltip is visible while mouse on it - visible_time_near, // time for which tooltip is visible while mouse near it - visible_time_far; // time for which tooltip is visible while mouse moved away - Optional sticky_rect; - Optional font; - Optional image; - Optional text_color; - Optional time_based_media, - web_based_media, - media_playing; - Optional create_callback; - Optional create_params; - Optional click_callback, - click_playmedia_callback, - click_homepage_callback; - Optional max_width, - padding; - Optional wrap; - - Optional allow_paste_tooltip; - - Params(); - }; - /*virtual*/ void draw(); - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); - /*virtual*/ void setVisible(bool visible); - - bool isFading(); - F32 getVisibleTime(); - bool hasClickCallback(); - - LLToolTip(const Params& p); - virtual void initFromParams(const LLToolTip::Params& params); - - void getToolTipMessage(std::string & message); - bool isTooltipPastable() { return mIsTooltipPastable; } - -protected: - void updateTextBox(); - void snapToChildren(); - -protected: - class LLTextBox* mTextBox; - class LLButton* mInfoButton; - class LLButton* mPlayMediaButton; - class LLButton* mHomePageButton; - - LLFrameTimer mFadeTimer; - LLFrameTimer mVisibleTimer; - bool mHasClickCallback; - S32 mPadding; // pixels - S32 mMaxWidth; - - bool mIsTooltipPastable; -}; - -// used for the inspector tooltips which need different background images etc. -class LLInspector : public LLToolTip -{ -public: - struct Params : public LLInitParam::Block - {}; -}; - -class LLToolTipMgr : public LLSingleton -{ - LLSINGLETON(LLToolTipMgr); - LOG_CLASS(LLToolTipMgr); - -public: - void show(const LLToolTip::Params& params); - void show(const std::string& message, bool allow_paste_tooltip = false); - - void unblockToolTips(); - void blockToolTips(); - - void hideToolTips(); - bool toolTipVisible(); - LLRect getToolTipRect(); - LLRect getMouseNearRect(); - void updateToolTipVisibility(); - - void getToolTipMessage(std::string & message); - bool isTooltipPastable(); - -private: - void createToolTip(const LLToolTip::Params& params); - - bool mToolTipsBlocked; - class LLToolTip* mToolTip; - - // tooltip creation is deferred until the UI is drawn every frame - // so the last tooltip to be created in a given frame will win - LLToolTip::Params mLastToolTipParams; // description of last tooltip we showed - LLToolTip::Params mNextToolTipParams; // description of next tooltip we want to show - bool mNeedsToolTip; // do we want to show a tooltip - - LLRect mMouseNearRect; -}; - -// -// Globals -// - -extern LLToolTipView *gToolTipView; - -#endif +/** + * @file lltooltip.h + * @brief LLToolTipMgr class definition and related classes + * + * $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$ + */ + +#ifndef LL_LLTOOLTIP_H +#define LL_LLTOOLTIP_H + +// Library includes +#include "llsingleton.h" +#include "llinitparam.h" +#include "llpanel.h" +#include "llstyle.h" + +// +// Classes +// +class LLToolTipView : public LLView +{ +public: + struct Params : public LLInitParam::Block + { + Params(); + }; + LLToolTipView(const LLToolTipView::Params&); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks ); + + void drawStickyRect(); + + /*virtual*/ void draw(); +}; + +class LLToolTip : public LLPanel +{ +public: + + struct StyledText : public LLInitParam::Block + { + Mandatory text; + Optional style; + }; + + struct Params : public LLInitParam::Block + { + typedef boost::function click_callback_t; + typedef boost::function create_callback_t; + + Optional message; + Multiple styled_message; + + Optional pos; + Optional delay_time, + visible_time_over, // time for which tooltip is visible while mouse on it + visible_time_near, // time for which tooltip is visible while mouse near it + visible_time_far; // time for which tooltip is visible while mouse moved away + Optional sticky_rect; + Optional font; + Optional image; + Optional text_color; + Optional time_based_media, + web_based_media, + media_playing; + Optional create_callback; + Optional create_params; + Optional click_callback, + click_playmedia_callback, + click_homepage_callback; + Optional max_width, + padding; + Optional wrap; + + Optional allow_paste_tooltip; + + Params(); + }; + /*virtual*/ void draw(); + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); + /*virtual*/ void setVisible(bool visible); + + bool isFading(); + F32 getVisibleTime(); + bool hasClickCallback(); + + LLToolTip(const Params& p); + virtual void initFromParams(const LLToolTip::Params& params); + + void getToolTipMessage(std::string & message); + bool isTooltipPastable() { return mIsTooltipPastable; } + +protected: + void updateTextBox(); + void snapToChildren(); + +protected: + class LLTextBox* mTextBox; + class LLButton* mInfoButton; + class LLButton* mPlayMediaButton; + class LLButton* mHomePageButton; + + LLFrameTimer mFadeTimer; + LLFrameTimer mVisibleTimer; + bool mHasClickCallback; + S32 mPadding; // pixels + S32 mMaxWidth; + + bool mIsTooltipPastable; +}; + +// used for the inspector tooltips which need different background images etc. +class LLInspector : public LLToolTip +{ +public: + struct Params : public LLInitParam::Block + {}; +}; + +class LLToolTipMgr : public LLSingleton +{ + LLSINGLETON(LLToolTipMgr); + LOG_CLASS(LLToolTipMgr); + +public: + void show(const LLToolTip::Params& params); + void show(const std::string& message, bool allow_paste_tooltip = false); + + void unblockToolTips(); + void blockToolTips(); + + void hideToolTips(); + bool toolTipVisible(); + LLRect getToolTipRect(); + LLRect getMouseNearRect(); + void updateToolTipVisibility(); + + void getToolTipMessage(std::string & message); + bool isTooltipPastable(); + +private: + void createToolTip(const LLToolTip::Params& params); + + bool mToolTipsBlocked; + class LLToolTip* mToolTip; + + // tooltip creation is deferred until the UI is drawn every frame + // so the last tooltip to be created in a given frame will win + LLToolTip::Params mLastToolTipParams; // description of last tooltip we showed + LLToolTip::Params mNextToolTipParams; // description of next tooltip we want to show + bool mNeedsToolTip; // do we want to show a tooltip + + LLRect mMouseNearRect; +}; + +// +// Globals +// + +extern LLToolTipView *gToolTipView; + +#endif diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp index 7f44f05f2b..4af5376a8b 100644 --- a/indra/llui/lltransutil.cpp +++ b/indra/llui/lltransutil.cpp @@ -1,74 +1,74 @@ -/** - * @file lltrans.cpp - * @brief LLTrans implementation - * - * $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$ - */ - -#include "linden_common.h" - -#include "lltransutil.h" - -#include "lltrans.h" -#include "lluictrlfactory.h" -#include "llxmlnode.h" -#include "lldir.h" - -bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set& default_args) -{ - LLXMLNodePtr root; - // Pass LLDir::ALL_SKINS to load a composite of all the individual string - // definitions in the default skin and the current skin. This means an - // individual skin can provide an xml_filename that overrides only a - // subset of the available string definitions; any string definition not - // overridden by that skin will be sought in the default skin. - bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS); - if (!success) - { - const std::string error_string = - "Second Life viewer couldn't access some of the files it needs and will be closed." - "\n\nPlease reinstall viewer from https://secondlife.com/support/downloads/ and " - "contact https://support.secondlife.com if issue persists after reinstall."; - LLError::LLUserWarningMsg::show(error_string); - gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN); - LL_ERRS() << "Couldn't load string table " << xml_filename << " " << errno << LL_ENDL; - return false; - } - - return LLTrans::parseStrings(root, default_args); -} - - -bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename) -{ - LLXMLNodePtr root; - bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); - - if (!success) - { - LLError::LLUserWarningMsg::showMissingFiles(); - LL_ERRS() << "Couldn't load localization table " << xml_filename << LL_ENDL; - return false; - } - - return LLTrans::parseLanguageStrings(root); -} +/** + * @file lltrans.cpp + * @brief LLTrans implementation + * + * $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$ + */ + +#include "linden_common.h" + +#include "lltransutil.h" + +#include "lltrans.h" +#include "lluictrlfactory.h" +#include "llxmlnode.h" +#include "lldir.h" + +bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set& default_args) +{ + LLXMLNodePtr root; + // Pass LLDir::ALL_SKINS to load a composite of all the individual string + // definitions in the default skin and the current skin. This means an + // individual skin can provide an xml_filename that overrides only a + // subset of the available string definitions; any string definition not + // overridden by that skin will be sought in the default skin. + bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS); + if (!success) + { + const std::string error_string = + "Second Life viewer couldn't access some of the files it needs and will be closed." + "\n\nPlease reinstall viewer from https://secondlife.com/support/downloads/ and " + "contact https://support.secondlife.com if issue persists after reinstall."; + LLError::LLUserWarningMsg::show(error_string); + gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN); + LL_ERRS() << "Couldn't load string table " << xml_filename << " " << errno << LL_ENDL; + return false; + } + + return LLTrans::parseStrings(root, default_args); +} + + +bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename) +{ + LLXMLNodePtr root; + bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); + + if (!success) + { + LLError::LLUserWarningMsg::showMissingFiles(); + LL_ERRS() << "Couldn't load localization table " << xml_filename << LL_ENDL; + return false; + } + + return LLTrans::parseLanguageStrings(root); +} diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 325dccf941..66ec3ad9bd 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1,738 +1,738 @@ -/** - * @file llui.cpp - * @brief UI 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$ - */ - -// Utilities functions the user interface needs - -#include "linden_common.h" - -#include -#include - -// Linden library includes -#include "v2math.h" -#include "m3math.h" -#include "v4color.h" -#include "llrender.h" -#include "llrect.h" -#include "lldir.h" -#include "llgl.h" -#include "llsd.h" - -// Project includes -#include "llcommandmanager.h" -#include "llcontrol.h" -#include "llui.h" -#include "lluicolortable.h" -#include "llview.h" -#include "lllineeditor.h" -#include "llfloater.h" -#include "llfloaterreg.h" -#include "llmenugl.h" -#include "llmenubutton.h" -#include "llloadingindicator.h" -#include "llwindow.h" - -// for registration -#include "llfiltereditor.h" -#include "llflyoutbutton.h" -#include "llsearcheditor.h" -#include "lltoolbar.h" -#include "llcleanup.h" - -// for XUIParse -#include "llquaternion.h" -#include -#include -#include - -// -// Globals -// - -// Language for UI construction -std::map gTranslation; -std::list gUntranslated; - -// register filter editor here -static LLDefaultChildRegistry::Register register_filter_editor("filter_editor"); -static LLDefaultChildRegistry::Register register_flyout_button("flyout_button"); -static LLDefaultChildRegistry::Register register_search_editor("search_editor"); - -// register other widgets which otherwise may not be linked in -static LLDefaultChildRegistry::Register register_loading_indicator("loading_indicator"); -static LLDefaultChildRegistry::Register register_toolbar("toolbar"); - -// -// Functions -// - -LLUUID find_ui_sound(const char * namep) -{ - std::string name = ll_safe_string(namep); - LLUUID uuid = LLUUID(NULL); - LLUI *ui_inst = LLUI::getInstance(); - if (!ui_inst->mSettingGroups["config"]->controlExists(name)) - { - LL_WARNS() << "tried to make UI sound for unknown sound name: " << name << LL_ENDL; - } - else - { - uuid = LLUUID(ui_inst->mSettingGroups["config"]->getString(name)); - if (uuid.isNull()) - { - if (ui_inst->mSettingGroups["config"]->getString(name) == LLUUID::null.asString()) - { - if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle")) - { - LL_INFOS() << "UI sound name: " << name << " triggered but silent (null uuid)" << LL_ENDL; - } - } - else - { - LL_WARNS() << "UI sound named: " << name << " does not translate to a valid uuid" << LL_ENDL; - } - } - else if (ui_inst->mAudioCallback != NULL) - { - if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle")) - { - LL_INFOS() << "UI sound name: " << name << LL_ENDL; - } - } - } - - return uuid; -} - -void make_ui_sound(const char* namep) -{ - LLUUID soundUUID = find_ui_sound(namep); - if(soundUUID.notNull()) - { - LLUI::getInstance()->mAudioCallback(soundUUID); - } -} - -void make_ui_sound_deferred(const char* namep) -{ - LLUUID soundUUID = find_ui_sound(namep); - if(soundUUID.notNull()) - { - LLUI::getInstance()->mDeferredAudioCallback(soundUUID); - } -} - -LLUI::LLUI(const settings_map_t& settings, - LLImageProviderInterface* image_provider, - LLUIAudioCallback audio_callback, - LLUIAudioCallback deferred_audio_callback) -: mSettingGroups(settings), -mAudioCallback(audio_callback), -mDeferredAudioCallback(deferred_audio_callback), -mWindow(NULL), // set later in startup -mRootView(NULL), -mHelpImpl(NULL) -{ - LLRender2D::initParamSingleton(image_provider); - - if ((get_ptr_in_map(mSettingGroups, std::string("config")) == NULL) || - (get_ptr_in_map(mSettingGroups, std::string("floater")) == NULL) || - (get_ptr_in_map(mSettingGroups, std::string("ignores")) == NULL)) - { - LL_ERRS() << "Failure to initialize configuration groups" << LL_ENDL; - } - - LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow"); - - LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar(); - - // Callbacks for associating controls with floater visibility: - reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleInstance, _2, LLSD())); - reg.add("Floater.ToggleOrBringToFront", boost::bind(&LLFloaterReg::toggleInstanceOrBringToFront, _2, LLSD())); - reg.add("Floater.Show", boost::bind(&LLFloaterReg::showInstance, _2, LLSD(), false)); - reg.add("Floater.ShowOrBringToFront", boost::bind(&LLFloaterReg::showInstanceOrBringToFront, _2, LLSD())); - reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideInstance, _2, LLSD())); - - // Button initialization callback for toggle buttons - reg.add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2)); - - // Button initialization callback for toggle buttons on dockable floaters - reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2)); - - // Display the help topic for the current context - reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2)); - - // Currently unused, but kept for reference: - reg.add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2)); - - // Used by menus along with Floater.Toggle to display visibility as a check-mark - LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::instanceVisible, _2, LLSD())); - LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.IsOpen", boost::bind(&LLFloaterReg::instanceVisible, _2, LLSD())); - - // Parse the master list of commands - LLCommandManager::load(); -} - -void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups) -{ - mAddPopupFunc = add_popup; - mRemovePopupFunc = remove_popup; - mClearPopupsFunc = clear_popups; -} - -void LLUI::setMousePositionScreen(S32 x, S32 y) -{ -#if defined(LL_DARWIN) - S32 screen_x = ll_round(((F32)x * getScaleFactor().mV[VX]) / LLView::getWindow()->getSystemUISize()); - S32 screen_y = ll_round(((F32)y * getScaleFactor().mV[VY]) / LLView::getWindow()->getSystemUISize()); -#else - S32 screen_x = ll_round((F32)x * getScaleFactor().mV[VX]); - S32 screen_y = ll_round((F32)y * getScaleFactor().mV[VY]); -#endif - - LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert()); -} - -void LLUI::getMousePositionScreen(S32 *x, S32 *y) -{ - LLCoordWindow cursor_pos_window; - getWindow()->getCursorPosition(&cursor_pos_window); - LLCoordGL cursor_pos_gl(cursor_pos_window.convert()); - *x = ll_round((F32)cursor_pos_gl.mX / getScaleFactor().mV[VX]); - *y = ll_round((F32)cursor_pos_gl.mY / getScaleFactor().mV[VY]); -} - -void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y) -{ - S32 screen_x, screen_y; - viewp->localPointToScreen(x, y, &screen_x, &screen_y); - - setMousePositionScreen(screen_x, screen_y); -} - -void LLUI::getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y) -{ - S32 screen_x, screen_y; - getMousePositionScreen(&screen_x, &screen_y); - viewp->screenPointToLocal(screen_x, screen_y, x, y); -} - - -// On Windows, the user typically sets the language when they install the -// app (by running it with a shortcut that sets InstallLanguage). On Mac, -// or on Windows if the SecondLife.exe executable is run directly, the -// language follows the OS language. In all cases the user can override -// the language manually in preferences. JC -std::string LLUI::getUILanguage() -{ - std::string language = "en"; - if (mSettingGroups["config"]) - { - language = mSettingGroups["config"]->getString("Language"); - if (language.empty() || language == "default") - { - language = mSettingGroups["config"]->getString("InstallLanguage"); - } - if (language.empty() || language == "default") - { - language = mSettingGroups["config"]->getString("SystemLanguage"); - } - if (language.empty() || language == "default") - { - language = "en"; - } - } - return language; -} - -// static -std::string LLUI::getLanguage() -{ - // Note: lldateutil_test redefines this function - return LLUI::getInstance()->getUILanguage(); -} - -struct SubDir : public LLInitParam::Block -{ - Mandatory value; - - SubDir() - : value("value") - {} -}; - -struct Directory : public LLInitParam::Block -{ - Multiple > subdirs; - - Directory() - : subdirs("subdir") - {} -}; - -struct Paths : public LLInitParam::Block -{ - Multiple > directories; - - Paths() - : directories("directory") - {} -}; - - -//static -std::string LLUI::locateSkin(const std::string& filename) -{ - std::string found_file = filename; - if (gDirUtilp->fileExists(found_file)) - { - return found_file; - } - - found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? - if (gDirUtilp->fileExists(found_file)) - { - return found_file; - } - - found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename); - if (! found_file.empty()) - { - return found_file; - } - - found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); - if (gDirUtilp->fileExists(found_file)) - { - return found_file; - } - LL_WARNS("LLUI") << "Can't find '" << filename - << "' in user settings, any skin directory or app_settings" << LL_ENDL; - return ""; -} - -LLVector2 LLUI::getWindowSize() -{ - LLCoordWindow window_rect; - mWindow->getSize(&window_rect); - - return LLVector2(window_rect.mX / getScaleFactor().mV[VX], window_rect.mY / getScaleFactor().mV[VY]); -} - -void LLUI::screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y) -{ - *gl_x = ll_round((F32)screen_x * getScaleFactor().mV[VX]); - *gl_y = ll_round((F32)screen_y * getScaleFactor().mV[VY]); -} - -void LLUI::glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y) -{ - *screen_x = ll_round((F32)gl_x / getScaleFactor().mV[VX]); - *screen_y = ll_round((F32)gl_y / getScaleFactor().mV[VY]); -} - -void LLUI::screenRectToGL(const LLRect& screen, LLRect *gl) -{ - screenPointToGL(screen.mLeft, screen.mTop, &gl->mLeft, &gl->mTop); - screenPointToGL(screen.mRight, screen.mBottom, &gl->mRight, &gl->mBottom); -} - -void LLUI::glRectToScreen(const LLRect& gl, LLRect *screen) -{ - glPointToScreen(gl.mLeft, gl.mTop, &screen->mLeft, &screen->mTop); - glPointToScreen(gl.mRight, gl.mBottom, &screen->mRight, &screen->mBottom); -} - - -LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname) -{ - for (settings_map_t::iterator itor = mSettingGroups.begin(); - itor != mSettingGroups.end(); ++itor) - { - LLControlGroup* control_group = itor->second; - if(control_group != NULL) - { - if (control_group->controlExists(controlname)) - return *control_group; - } - } - - return *mSettingGroups["config"]; // default group -} - -void LLUI::addPopup(LLView* viewp) -{ - if (mAddPopupFunc) - { - mAddPopupFunc(viewp); - } -} - -void LLUI::removePopup(LLView* viewp) -{ - if (mRemovePopupFunc) - { - mRemovePopupFunc(viewp); - } -} - -void LLUI::clearPopups() -{ - if (mClearPopupsFunc) - { - mClearPopupsFunc(); - } -} - -void LLUI::reportBadKeystroke() -{ - make_ui_sound("UISndBadKeystroke"); -} - -// spawn_x and spawn_y are top left corner of view in screen GL coordinates -void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y) -{ - const S32 CURSOR_HEIGHT = 16; // Approximate "normal" cursor size - const S32 CURSOR_WIDTH = 8; - - LLView* parent = view->getParent(); - - S32 mouse_x; - S32 mouse_y; - getMousePositionScreen(&mouse_x, &mouse_y); - - // If no spawn location provided, use mouse position - if (spawn_x == S32_MAX || spawn_y == S32_MAX) - { - spawn_x = mouse_x + CURSOR_WIDTH; - spawn_y = mouse_y - CURSOR_HEIGHT; - } - - LLRect virtual_window_rect = parent->getLocalRect(); - - LLRect mouse_rect; - const S32 MOUSE_CURSOR_PADDING = 1; - 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); - - S32 local_x, local_y; - // convert screen coordinates to tooltip view-local coordinates - parent->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y); - - // Start at spawn position (using left/top) - view->setOrigin( local_x, local_y - view->getRect().getHeight()); - // Make sure we're on-screen and not overlapping the mouse - view->translateIntoRectWithExclusion( virtual_window_rect, mouse_rect ); -} - -LLView* LLUI::resolvePath(LLView* context, const std::string& path) -{ - // Nothing about resolvePath() should require non-const LLView*. If caller - // wants non-const, call the const flavor and then cast away const-ness. - return const_cast(resolvePath(const_cast(context), path)); -} - -const LLView* LLUI::resolvePath(const LLView* context, const std::string& path) -{ - // Create an iterator over slash-separated parts of 'path'. Dereferencing - // this iterator returns an iterator_range over the substring. Unlike - // LLStringUtil::getTokens(), this split_iterator doesn't combine adjacent - // delimiters: leading/trailing slash produces an empty substring, double - // slash produces an empty substring. That's what we need. - boost::split_iterator ti(path, boost::first_finder("/")), tend; - - if (ti == tend) - { - // 'path' is completely empty, no navigation - return context; - } - - // leading / means "start at root" - if (ti->empty()) - { - context = getRootView(); - ++ti; - } - - bool recurse = false; - for (; ti != tend && context; ++ti) - { - if (ti->empty()) - { - recurse = true; - } - else - { - std::string part(ti->begin(), ti->end()); - context = context->findChildView(LLURI::unescape(part), recurse); - recurse = false; - } - } - - return context; -} - -//static -LLVector2& LLUI::getScaleFactor() -{ - return LLRender::sUIGLScaleFactor; -} - -//static -void LLUI::setScaleFactor(const LLVector2& scale_factor) -{ - LLRender::sUIGLScaleFactor = scale_factor; -} - - -// LLLocalClipRect and LLScreenClipRect moved to lllocalcliprect.h/cpp - -namespace LLInitParam -{ - ParamValue::ParamValue(const LLUIColor& color) - : super_t(color), - red("red"), - green("green"), - blue("blue"), - alpha("alpha"), - control("") - { - updateBlockFromValue(false); - } - - void ParamValue::updateValueFromBlock() - { - if (control.isProvided() && !control().empty()) - { - updateValue(LLUIColorTable::instance().getColor(control)); - } - else - { - updateValue(LLColor4(red, green, blue, alpha)); - } - } - - void ParamValue::updateBlockFromValue(bool make_block_authoritative) - { - LLColor4 color = getValue(); - red.set(color.mV[VRED], make_block_authoritative); - green.set(color.mV[VGREEN], make_block_authoritative); - blue.set(color.mV[VBLUE], make_block_authoritative); - alpha.set(color.mV[VALPHA], make_block_authoritative); - control.set("", make_block_authoritative); - } - - bool ParamCompare::equals(const LLFontGL* a, const LLFontGL* b) - { - return !(a->getFontDesc() < b->getFontDesc()) - && !(b->getFontDesc() < a->getFontDesc()); - } - - ParamValue::ParamValue(const LLFontGL* fontp) - : super_t(fontp), - name("name"), - size("size"), - style("style") - { - if (!fontp) - { - updateValue(LLFontGL::getFontDefault()); - } - addSynonym(name, ""); - updateBlockFromValue(false); - } - - void ParamValue::updateValueFromBlock() - { - const LLFontGL* res_fontp = LLFontGL::getFontByName(name); - if (res_fontp) - { - updateValue(res_fontp); - return; - } - - U8 fontstyle = 0; - fontstyle = LLFontGL::getStyleFromString(style()); - LLFontDescriptor desc(name(), size(), fontstyle); - const LLFontGL* fontp = LLFontGL::getFont(desc); - if (fontp) - { - updateValue(fontp); - } - else - { - updateValue(LLFontGL::getFontDefault()); - } - } - - void ParamValue::updateBlockFromValue(bool make_block_authoritative) - { - if (getValue()) - { - name.set(LLFontGL::nameFromFont(getValue()), make_block_authoritative); - size.set(LLFontGL::sizeFromFont(getValue()), make_block_authoritative); - style.set(LLFontGL::getStringFromStyle(getValue()->getFontDesc().getStyle()), make_block_authoritative); - } - } - - ParamValue::ParamValue(const LLRect& rect) - : super_t(rect), - left("left"), - top("top"), - right("right"), - bottom("bottom"), - width("width"), - height("height") - { - updateBlockFromValue(false); - } - - void ParamValue::updateValueFromBlock() - { - LLRect rect; - - //calculate from params - // prefer explicit left and right - if (left.isProvided() && right.isProvided()) - { - rect.mLeft = left; - rect.mRight = right; - } - // otherwise use width along with specified side, if any - else if (width.isProvided()) - { - // only right + width provided - if (right.isProvided()) - { - rect.mRight = right; - rect.mLeft = right - width; - } - else // left + width, or just width - { - rect.mLeft = left; - rect.mRight = left + width; - } - } - // just left, just right, or none - else - { - rect.mLeft = left; - rect.mRight = right; - } - - // prefer explicit bottom and top - if (bottom.isProvided() && top.isProvided()) - { - rect.mBottom = bottom; - rect.mTop = top; - } - // otherwise height along with specified side, if any - else if (height.isProvided()) - { - // top + height provided - if (top.isProvided()) - { - rect.mTop = top; - rect.mBottom = top - height; - } - // bottom + height or just height - else - { - rect.mBottom = bottom; - rect.mTop = bottom + height; - } - } - // just bottom, just top, or none - else - { - rect.mBottom = bottom; - rect.mTop = top; - } - updateValue(rect); - } - - void ParamValue::updateBlockFromValue(bool make_block_authoritative) - { - // because of the ambiguity in specifying a rect by position and/or dimensions - // we use the lowest priority pairing so that any valid pairing in xui - // will override those calculated from the rect object - // in this case, that is left+width and bottom+height - LLRect& value = getValue(); - - right.set(value.mRight, false); - left.set(value.mLeft, make_block_authoritative); - width.set(value.getWidth(), make_block_authoritative); - - top.set(value.mTop, false); - bottom.set(value.mBottom, make_block_authoritative); - height.set(value.getHeight(), make_block_authoritative); - } - - ParamValue::ParamValue(const LLCoordGL& coord) - : super_t(coord), - x("x"), - y("y") - { - updateBlockFromValue(false); - } - - void ParamValue::updateValueFromBlock() - { - updateValue(LLCoordGL(x, y)); - } - - void ParamValue::updateBlockFromValue(bool make_block_authoritative) - { - x.set(getValue().mX, make_block_authoritative); - y.set(getValue().mY, make_block_authoritative); - } - - - void TypeValues::declareValues() - { - declare("left", LLFontGL::LEFT); - declare("right", LLFontGL::RIGHT); - declare("center", LLFontGL::HCENTER); - } - - void TypeValues::declareValues() - { - declare("top", LLFontGL::TOP); - declare("center", LLFontGL::VCENTER); - declare("baseline", LLFontGL::BASELINE); - declare("bottom", LLFontGL::BOTTOM); - } - - void TypeValues::declareValues() - { - declare("none", LLFontGL::NO_SHADOW); - declare("hard", LLFontGL::DROP_SHADOW); - declare("soft", LLFontGL::DROP_SHADOW_SOFT); - } -} - +/** + * @file llui.cpp + * @brief UI 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$ + */ + +// Utilities functions the user interface needs + +#include "linden_common.h" + +#include +#include + +// Linden library includes +#include "v2math.h" +#include "m3math.h" +#include "v4color.h" +#include "llrender.h" +#include "llrect.h" +#include "lldir.h" +#include "llgl.h" +#include "llsd.h" + +// Project includes +#include "llcommandmanager.h" +#include "llcontrol.h" +#include "llui.h" +#include "lluicolortable.h" +#include "llview.h" +#include "lllineeditor.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "llmenugl.h" +#include "llmenubutton.h" +#include "llloadingindicator.h" +#include "llwindow.h" + +// for registration +#include "llfiltereditor.h" +#include "llflyoutbutton.h" +#include "llsearcheditor.h" +#include "lltoolbar.h" +#include "llcleanup.h" + +// for XUIParse +#include "llquaternion.h" +#include +#include +#include + +// +// Globals +// + +// Language for UI construction +std::map gTranslation; +std::list gUntranslated; + +// register filter editor here +static LLDefaultChildRegistry::Register register_filter_editor("filter_editor"); +static LLDefaultChildRegistry::Register register_flyout_button("flyout_button"); +static LLDefaultChildRegistry::Register register_search_editor("search_editor"); + +// register other widgets which otherwise may not be linked in +static LLDefaultChildRegistry::Register register_loading_indicator("loading_indicator"); +static LLDefaultChildRegistry::Register register_toolbar("toolbar"); + +// +// Functions +// + +LLUUID find_ui_sound(const char * namep) +{ + std::string name = ll_safe_string(namep); + LLUUID uuid = LLUUID(NULL); + LLUI *ui_inst = LLUI::getInstance(); + if (!ui_inst->mSettingGroups["config"]->controlExists(name)) + { + LL_WARNS() << "tried to make UI sound for unknown sound name: " << name << LL_ENDL; + } + else + { + uuid = LLUUID(ui_inst->mSettingGroups["config"]->getString(name)); + if (uuid.isNull()) + { + if (ui_inst->mSettingGroups["config"]->getString(name) == LLUUID::null.asString()) + { + if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle")) + { + LL_INFOS() << "UI sound name: " << name << " triggered but silent (null uuid)" << LL_ENDL; + } + } + else + { + LL_WARNS() << "UI sound named: " << name << " does not translate to a valid uuid" << LL_ENDL; + } + } + else if (ui_inst->mAudioCallback != NULL) + { + if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle")) + { + LL_INFOS() << "UI sound name: " << name << LL_ENDL; + } + } + } + + return uuid; +} + +void make_ui_sound(const char* namep) +{ + LLUUID soundUUID = find_ui_sound(namep); + if(soundUUID.notNull()) + { + LLUI::getInstance()->mAudioCallback(soundUUID); + } +} + +void make_ui_sound_deferred(const char* namep) +{ + LLUUID soundUUID = find_ui_sound(namep); + if(soundUUID.notNull()) + { + LLUI::getInstance()->mDeferredAudioCallback(soundUUID); + } +} + +LLUI::LLUI(const settings_map_t& settings, + LLImageProviderInterface* image_provider, + LLUIAudioCallback audio_callback, + LLUIAudioCallback deferred_audio_callback) +: mSettingGroups(settings), +mAudioCallback(audio_callback), +mDeferredAudioCallback(deferred_audio_callback), +mWindow(NULL), // set later in startup +mRootView(NULL), +mHelpImpl(NULL) +{ + LLRender2D::initParamSingleton(image_provider); + + if ((get_ptr_in_map(mSettingGroups, std::string("config")) == NULL) || + (get_ptr_in_map(mSettingGroups, std::string("floater")) == NULL) || + (get_ptr_in_map(mSettingGroups, std::string("ignores")) == NULL)) + { + LL_ERRS() << "Failure to initialize configuration groups" << LL_ENDL; + } + + LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow"); + + LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar(); + + // Callbacks for associating controls with floater visibility: + reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleInstance, _2, LLSD())); + reg.add("Floater.ToggleOrBringToFront", boost::bind(&LLFloaterReg::toggleInstanceOrBringToFront, _2, LLSD())); + reg.add("Floater.Show", boost::bind(&LLFloaterReg::showInstance, _2, LLSD(), false)); + reg.add("Floater.ShowOrBringToFront", boost::bind(&LLFloaterReg::showInstanceOrBringToFront, _2, LLSD())); + reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideInstance, _2, LLSD())); + + // Button initialization callback for toggle buttons + reg.add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2)); + + // Button initialization callback for toggle buttons on dockable floaters + reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2)); + + // Display the help topic for the current context + reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2)); + + // Currently unused, but kept for reference: + reg.add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2)); + + // Used by menus along with Floater.Toggle to display visibility as a check-mark + LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::instanceVisible, _2, LLSD())); + LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.IsOpen", boost::bind(&LLFloaterReg::instanceVisible, _2, LLSD())); + + // Parse the master list of commands + LLCommandManager::load(); +} + +void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups) +{ + mAddPopupFunc = add_popup; + mRemovePopupFunc = remove_popup; + mClearPopupsFunc = clear_popups; +} + +void LLUI::setMousePositionScreen(S32 x, S32 y) +{ +#if defined(LL_DARWIN) + S32 screen_x = ll_round(((F32)x * getScaleFactor().mV[VX]) / LLView::getWindow()->getSystemUISize()); + S32 screen_y = ll_round(((F32)y * getScaleFactor().mV[VY]) / LLView::getWindow()->getSystemUISize()); +#else + S32 screen_x = ll_round((F32)x * getScaleFactor().mV[VX]); + S32 screen_y = ll_round((F32)y * getScaleFactor().mV[VY]); +#endif + + LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert()); +} + +void LLUI::getMousePositionScreen(S32 *x, S32 *y) +{ + LLCoordWindow cursor_pos_window; + getWindow()->getCursorPosition(&cursor_pos_window); + LLCoordGL cursor_pos_gl(cursor_pos_window.convert()); + *x = ll_round((F32)cursor_pos_gl.mX / getScaleFactor().mV[VX]); + *y = ll_round((F32)cursor_pos_gl.mY / getScaleFactor().mV[VY]); +} + +void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y) +{ + S32 screen_x, screen_y; + viewp->localPointToScreen(x, y, &screen_x, &screen_y); + + setMousePositionScreen(screen_x, screen_y); +} + +void LLUI::getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y) +{ + S32 screen_x, screen_y; + getMousePositionScreen(&screen_x, &screen_y); + viewp->screenPointToLocal(screen_x, screen_y, x, y); +} + + +// On Windows, the user typically sets the language when they install the +// app (by running it with a shortcut that sets InstallLanguage). On Mac, +// or on Windows if the SecondLife.exe executable is run directly, the +// language follows the OS language. In all cases the user can override +// the language manually in preferences. JC +std::string LLUI::getUILanguage() +{ + std::string language = "en"; + if (mSettingGroups["config"]) + { + language = mSettingGroups["config"]->getString("Language"); + if (language.empty() || language == "default") + { + language = mSettingGroups["config"]->getString("InstallLanguage"); + } + if (language.empty() || language == "default") + { + language = mSettingGroups["config"]->getString("SystemLanguage"); + } + if (language.empty() || language == "default") + { + language = "en"; + } + } + return language; +} + +// static +std::string LLUI::getLanguage() +{ + // Note: lldateutil_test redefines this function + return LLUI::getInstance()->getUILanguage(); +} + +struct SubDir : public LLInitParam::Block +{ + Mandatory value; + + SubDir() + : value("value") + {} +}; + +struct Directory : public LLInitParam::Block +{ + Multiple > subdirs; + + Directory() + : subdirs("subdir") + {} +}; + +struct Paths : public LLInitParam::Block +{ + Multiple > directories; + + Paths() + : directories("directory") + {} +}; + + +//static +std::string LLUI::locateSkin(const std::string& filename) +{ + std::string found_file = filename; + if (gDirUtilp->fileExists(found_file)) + { + return found_file; + } + + found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? + if (gDirUtilp->fileExists(found_file)) + { + return found_file; + } + + found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename); + if (! found_file.empty()) + { + return found_file; + } + + found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); + if (gDirUtilp->fileExists(found_file)) + { + return found_file; + } + LL_WARNS("LLUI") << "Can't find '" << filename + << "' in user settings, any skin directory or app_settings" << LL_ENDL; + return ""; +} + +LLVector2 LLUI::getWindowSize() +{ + LLCoordWindow window_rect; + mWindow->getSize(&window_rect); + + return LLVector2(window_rect.mX / getScaleFactor().mV[VX], window_rect.mY / getScaleFactor().mV[VY]); +} + +void LLUI::screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y) +{ + *gl_x = ll_round((F32)screen_x * getScaleFactor().mV[VX]); + *gl_y = ll_round((F32)screen_y * getScaleFactor().mV[VY]); +} + +void LLUI::glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y) +{ + *screen_x = ll_round((F32)gl_x / getScaleFactor().mV[VX]); + *screen_y = ll_round((F32)gl_y / getScaleFactor().mV[VY]); +} + +void LLUI::screenRectToGL(const LLRect& screen, LLRect *gl) +{ + screenPointToGL(screen.mLeft, screen.mTop, &gl->mLeft, &gl->mTop); + screenPointToGL(screen.mRight, screen.mBottom, &gl->mRight, &gl->mBottom); +} + +void LLUI::glRectToScreen(const LLRect& gl, LLRect *screen) +{ + glPointToScreen(gl.mLeft, gl.mTop, &screen->mLeft, &screen->mTop); + glPointToScreen(gl.mRight, gl.mBottom, &screen->mRight, &screen->mBottom); +} + + +LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname) +{ + for (settings_map_t::iterator itor = mSettingGroups.begin(); + itor != mSettingGroups.end(); ++itor) + { + LLControlGroup* control_group = itor->second; + if(control_group != NULL) + { + if (control_group->controlExists(controlname)) + return *control_group; + } + } + + return *mSettingGroups["config"]; // default group +} + +void LLUI::addPopup(LLView* viewp) +{ + if (mAddPopupFunc) + { + mAddPopupFunc(viewp); + } +} + +void LLUI::removePopup(LLView* viewp) +{ + if (mRemovePopupFunc) + { + mRemovePopupFunc(viewp); + } +} + +void LLUI::clearPopups() +{ + if (mClearPopupsFunc) + { + mClearPopupsFunc(); + } +} + +void LLUI::reportBadKeystroke() +{ + make_ui_sound("UISndBadKeystroke"); +} + +// spawn_x and spawn_y are top left corner of view in screen GL coordinates +void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y) +{ + const S32 CURSOR_HEIGHT = 16; // Approximate "normal" cursor size + const S32 CURSOR_WIDTH = 8; + + LLView* parent = view->getParent(); + + S32 mouse_x; + S32 mouse_y; + getMousePositionScreen(&mouse_x, &mouse_y); + + // If no spawn location provided, use mouse position + if (spawn_x == S32_MAX || spawn_y == S32_MAX) + { + spawn_x = mouse_x + CURSOR_WIDTH; + spawn_y = mouse_y - CURSOR_HEIGHT; + } + + LLRect virtual_window_rect = parent->getLocalRect(); + + LLRect mouse_rect; + const S32 MOUSE_CURSOR_PADDING = 1; + 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); + + S32 local_x, local_y; + // convert screen coordinates to tooltip view-local coordinates + parent->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y); + + // Start at spawn position (using left/top) + view->setOrigin( local_x, local_y - view->getRect().getHeight()); + // Make sure we're on-screen and not overlapping the mouse + view->translateIntoRectWithExclusion( virtual_window_rect, mouse_rect ); +} + +LLView* LLUI::resolvePath(LLView* context, const std::string& path) +{ + // Nothing about resolvePath() should require non-const LLView*. If caller + // wants non-const, call the const flavor and then cast away const-ness. + return const_cast(resolvePath(const_cast(context), path)); +} + +const LLView* LLUI::resolvePath(const LLView* context, const std::string& path) +{ + // Create an iterator over slash-separated parts of 'path'. Dereferencing + // this iterator returns an iterator_range over the substring. Unlike + // LLStringUtil::getTokens(), this split_iterator doesn't combine adjacent + // delimiters: leading/trailing slash produces an empty substring, double + // slash produces an empty substring. That's what we need. + boost::split_iterator ti(path, boost::first_finder("/")), tend; + + if (ti == tend) + { + // 'path' is completely empty, no navigation + return context; + } + + // leading / means "start at root" + if (ti->empty()) + { + context = getRootView(); + ++ti; + } + + bool recurse = false; + for (; ti != tend && context; ++ti) + { + if (ti->empty()) + { + recurse = true; + } + else + { + std::string part(ti->begin(), ti->end()); + context = context->findChildView(LLURI::unescape(part), recurse); + recurse = false; + } + } + + return context; +} + +//static +LLVector2& LLUI::getScaleFactor() +{ + return LLRender::sUIGLScaleFactor; +} + +//static +void LLUI::setScaleFactor(const LLVector2& scale_factor) +{ + LLRender::sUIGLScaleFactor = scale_factor; +} + + +// LLLocalClipRect and LLScreenClipRect moved to lllocalcliprect.h/cpp + +namespace LLInitParam +{ + ParamValue::ParamValue(const LLUIColor& color) + : super_t(color), + red("red"), + green("green"), + blue("blue"), + alpha("alpha"), + control("") + { + updateBlockFromValue(false); + } + + void ParamValue::updateValueFromBlock() + { + if (control.isProvided() && !control().empty()) + { + updateValue(LLUIColorTable::instance().getColor(control)); + } + else + { + updateValue(LLColor4(red, green, blue, alpha)); + } + } + + void ParamValue::updateBlockFromValue(bool make_block_authoritative) + { + LLColor4 color = getValue(); + red.set(color.mV[VRED], make_block_authoritative); + green.set(color.mV[VGREEN], make_block_authoritative); + blue.set(color.mV[VBLUE], make_block_authoritative); + alpha.set(color.mV[VALPHA], make_block_authoritative); + control.set("", make_block_authoritative); + } + + bool ParamCompare::equals(const LLFontGL* a, const LLFontGL* b) + { + return !(a->getFontDesc() < b->getFontDesc()) + && !(b->getFontDesc() < a->getFontDesc()); + } + + ParamValue::ParamValue(const LLFontGL* fontp) + : super_t(fontp), + name("name"), + size("size"), + style("style") + { + if (!fontp) + { + updateValue(LLFontGL::getFontDefault()); + } + addSynonym(name, ""); + updateBlockFromValue(false); + } + + void ParamValue::updateValueFromBlock() + { + const LLFontGL* res_fontp = LLFontGL::getFontByName(name); + if (res_fontp) + { + updateValue(res_fontp); + return; + } + + U8 fontstyle = 0; + fontstyle = LLFontGL::getStyleFromString(style()); + LLFontDescriptor desc(name(), size(), fontstyle); + const LLFontGL* fontp = LLFontGL::getFont(desc); + if (fontp) + { + updateValue(fontp); + } + else + { + updateValue(LLFontGL::getFontDefault()); + } + } + + void ParamValue::updateBlockFromValue(bool make_block_authoritative) + { + if (getValue()) + { + name.set(LLFontGL::nameFromFont(getValue()), make_block_authoritative); + size.set(LLFontGL::sizeFromFont(getValue()), make_block_authoritative); + style.set(LLFontGL::getStringFromStyle(getValue()->getFontDesc().getStyle()), make_block_authoritative); + } + } + + ParamValue::ParamValue(const LLRect& rect) + : super_t(rect), + left("left"), + top("top"), + right("right"), + bottom("bottom"), + width("width"), + height("height") + { + updateBlockFromValue(false); + } + + void ParamValue::updateValueFromBlock() + { + LLRect rect; + + //calculate from params + // prefer explicit left and right + if (left.isProvided() && right.isProvided()) + { + rect.mLeft = left; + rect.mRight = right; + } + // otherwise use width along with specified side, if any + else if (width.isProvided()) + { + // only right + width provided + if (right.isProvided()) + { + rect.mRight = right; + rect.mLeft = right - width; + } + else // left + width, or just width + { + rect.mLeft = left; + rect.mRight = left + width; + } + } + // just left, just right, or none + else + { + rect.mLeft = left; + rect.mRight = right; + } + + // prefer explicit bottom and top + if (bottom.isProvided() && top.isProvided()) + { + rect.mBottom = bottom; + rect.mTop = top; + } + // otherwise height along with specified side, if any + else if (height.isProvided()) + { + // top + height provided + if (top.isProvided()) + { + rect.mTop = top; + rect.mBottom = top - height; + } + // bottom + height or just height + else + { + rect.mBottom = bottom; + rect.mTop = bottom + height; + } + } + // just bottom, just top, or none + else + { + rect.mBottom = bottom; + rect.mTop = top; + } + updateValue(rect); + } + + void ParamValue::updateBlockFromValue(bool make_block_authoritative) + { + // because of the ambiguity in specifying a rect by position and/or dimensions + // we use the lowest priority pairing so that any valid pairing in xui + // will override those calculated from the rect object + // in this case, that is left+width and bottom+height + LLRect& value = getValue(); + + right.set(value.mRight, false); + left.set(value.mLeft, make_block_authoritative); + width.set(value.getWidth(), make_block_authoritative); + + top.set(value.mTop, false); + bottom.set(value.mBottom, make_block_authoritative); + height.set(value.getHeight(), make_block_authoritative); + } + + ParamValue::ParamValue(const LLCoordGL& coord) + : super_t(coord), + x("x"), + y("y") + { + updateBlockFromValue(false); + } + + void ParamValue::updateValueFromBlock() + { + updateValue(LLCoordGL(x, y)); + } + + void ParamValue::updateBlockFromValue(bool make_block_authoritative) + { + x.set(getValue().mX, make_block_authoritative); + y.set(getValue().mY, make_block_authoritative); + } + + + void TypeValues::declareValues() + { + declare("left", LLFontGL::LEFT); + declare("right", LLFontGL::RIGHT); + declare("center", LLFontGL::HCENTER); + } + + void TypeValues::declareValues() + { + declare("top", LLFontGL::TOP); + declare("center", LLFontGL::VCENTER); + declare("baseline", LLFontGL::BASELINE); + declare("bottom", LLFontGL::BOTTOM); + } + + void TypeValues::declareValues() + { + declare("none", LLFontGL::NO_SHADOW); + declare("hard", LLFontGL::DROP_SHADOW); + declare("soft", LLFontGL::DROP_SHADOW_SOFT); + } +} + diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 4c2ad9d92b..52c5f72a45 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -1,1137 +1,1137 @@ -/** - * @file lluictrl.cpp - * @author James Cook, Richard Nelson, Tom Yedwab - * @brief Abstract base class for UI controls - * - * $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 "linden_common.h" - -#define LLUICTRL_CPP -#include "lluictrl.h" -#include "llviewereventrecorder.h" -#include "llfocusmgr.h" -#include "llpanel.h" -#include "lluictrlfactory.h" -#include "lltabcontainer.h" -#include "llaccordionctrltab.h" -#include "lluiusage.h" - -static LLDefaultChildRegistry::Register r("ui_ctrl"); - -F32 LLUICtrl::sActiveControlTransparency = 1.0f; -F32 LLUICtrl::sInactiveControlTransparency = 1.0f; - -// Compiler optimization, generate extern template -template class LLUICtrl* LLView::getChild( - const std::string& name, bool recurse) const; - -LLUICtrl::CallbackParam::CallbackParam() -: name("name"), - function_name("function"), - parameter("parameter"), - control_name("control") // Shortcut to control -> "control_name" for backwards compatability -{ - addSynonym(parameter, "userdata"); -} - -LLUICtrl::EnableControls::EnableControls() -: enabled("enabled_control"), - disabled("disabled_control") -{} - -LLUICtrl::ControlVisibility::ControlVisibility() -: visible("visibility_control"), - invisible("invisibility_control") -{ - addSynonym(visible, "visiblity_control"); - addSynonym(invisible, "invisiblity_control"); -} - -LLUICtrl::Params::Params() -: tab_stop("tab_stop", true), - chrome("chrome", false), - requests_front("requests_front", false), - label("label"), - initial_value("value"), - init_callback("init_callback"), - commit_callback("commit_callback"), - validate_callback("validate_callback"), - mouseenter_callback("mouseenter_callback"), - mouseleave_callback("mouseleave_callback"), - control_name("control_name"), - font("font", LLFontGL::getFontEmojiMedium()), - font_halign("halign"), - font_valign("valign"), - length("length"), // ignore LLXMLNode cruft - type("type") // ignore LLXMLNode cruft -{ - addSynonym(initial_value, "initial_value"); -} - -// NOTE: the LLFocusableElement implementation has been moved from here to llfocusmgr.cpp. - -//static -const LLUICtrl::Params& LLUICtrl::getDefaultParams() -{ - return LLUICtrlFactory::getDefaultParams(); -} - - -LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) -: LLView(p), - mIsChrome(false), - mRequestsFront(p.requests_front), - mTabStop(false), - mTentative(false), - mViewModel(viewmodel), - mControlVariable(NULL), - mEnabledControlVariable(NULL), - mDisabledControlVariable(NULL), - mMakeVisibleControlVariable(NULL), - mMakeInvisibleControlVariable(NULL), - mCommitSignal(NULL), - mValidateSignal(NULL), - mMouseEnterSignal(NULL), - mMouseLeaveSignal(NULL), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mRightMouseDownSignal(NULL), - mRightMouseUpSignal(NULL), - mDoubleClickSignal(NULL), - mTransparencyType(TT_DEFAULT) -{ -} - -void LLUICtrl::initFromParams(const Params& p) -{ - LLView::initFromParams(p); - - mRequestsFront = p.requests_front; - - setIsChrome(p.chrome); - setControlName(p.control_name); - if(p.enabled_controls.isProvided()) - { - if (p.enabled_controls.enabled.isChosen()) - { - LLControlVariable* control = findControl(p.enabled_controls.enabled); - if (control) - { - setEnabledControlVariable(control); - } - else - { - LL_WARNS() << "Failed to assign 'enabled' control variable to " << getName() - << ": control " << p.enabled_controls.enabled() - << " does not exist." << LL_ENDL; - } - } - else if(p.enabled_controls.disabled.isChosen()) - { - LLControlVariable* control = findControl(p.enabled_controls.disabled); - if (control) - { - setDisabledControlVariable(control); - } - else - { - LL_WARNS() << "Failed to assign 'disabled' control variable to " << getName() - << ": control " << p.enabled_controls.disabled() - << " does not exist." << LL_ENDL; - } - } - } - if(p.controls_visibility.isProvided()) - { - if (p.controls_visibility.visible.isChosen()) - { - LLControlVariable* control = findControl(p.controls_visibility.visible); - if (control) - { - setMakeVisibleControlVariable(control); - } - else - { - LL_WARNS() << "Failed to assign visibility control variable to " << getName() - << ": control " << p.controls_visibility.visible() - << " does not exist." << LL_ENDL; - } - } - else if (p.controls_visibility.invisible.isChosen()) - { - LLControlVariable* control = findControl(p.controls_visibility.invisible); - if (control) - { - setMakeInvisibleControlVariable(control); - } - else - { - LL_WARNS() << "Failed to assign invisibility control variable to " << getName() - << ": control " << p.controls_visibility.invisible() - << " does not exist." << LL_ENDL; - } - } - } - - setTabStop(p.tab_stop); - - if (p.initial_value.isProvided() - && !p.control_name.isProvided()) - { - setValue(p.initial_value); - } - - if (p.commit_callback.isProvided()) - { - setCommitCallback(initCommitCallback(p.commit_callback)); - } - - if (p.validate_callback.isProvided()) - { - setValidateCallback(initEnableCallback(p.validate_callback)); - } - - if (p.init_callback.isProvided()) - { - if (p.init_callback.function.isProvided()) - { - p.init_callback.function()(this, p.init_callback.parameter); - } - else - { - commit_callback_t* initfunc = (CommitCallbackRegistry::getValue(p.init_callback.function_name)); - if (initfunc) - { - (*initfunc)(this, p.init_callback.parameter); - } - } - } - - if(p.mouseenter_callback.isProvided()) - { - setMouseEnterCallback(initCommitCallback(p.mouseenter_callback)); - } - - if(p.mouseleave_callback.isProvided()) - { - setMouseLeaveCallback(initCommitCallback(p.mouseleave_callback)); - } -} - - -LLUICtrl::~LLUICtrl() -{ - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() - - if( gFocusMgr.getTopCtrl() == this ) - { - LL_WARNS() << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << LL_ENDL; - gFocusMgr.removeTopCtrlWithoutCallback( this ); - } - - delete mCommitSignal; - delete mValidateSignal; - delete mMouseEnterSignal; - delete mMouseLeaveSignal; - delete mMouseDownSignal; - delete mMouseUpSignal; - delete mRightMouseDownSignal; - delete mRightMouseUpSignal; - delete mDoubleClickSignal; -} - -void default_commit_handler(LLUICtrl* ctrl, const LLSD& param) -{} - -bool default_enable_handler(LLUICtrl* ctrl, const LLSD& param) -{ - return true; -} - - -LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCallbackParam& cb) -{ - if (cb.function.isProvided()) - { - if (cb.parameter.isProvided()) - return boost::bind(cb.function(), _1, cb.parameter); - else - return cb.function(); - } - else - { - std::string function_name = cb.function_name; - setFunctionName(function_name); - commit_callback_t* func = (CommitCallbackRegistry::getValue(function_name)); - if (func) - { - if (cb.parameter.isProvided()) - return boost::bind((*func), _1, cb.parameter); - else - return commit_signal_t::slot_type(*func); - } - else if (!function_name.empty()) - { - LL_WARNS() << "No callback found for: '" << function_name << "' in control: " << getName() << LL_ENDL; - } - } - return default_commit_handler; -} - -LLUICtrl::enable_signal_t::slot_type LLUICtrl::initEnableCallback(const EnableCallbackParam& cb) -{ - // Set the callback function - if (cb.function.isProvided()) - { - if (cb.parameter.isProvided()) - return boost::bind(cb.function(), this, cb.parameter); - else - return cb.function(); - } - else - { - enable_callback_t* func = (EnableCallbackRegistry::getValue(cb.function_name)); - if (func) - { - if (cb.parameter.isProvided()) - return boost::bind((*func), this, cb.parameter); - else - return enable_signal_t::slot_type(*func); - } - } - return default_enable_handler; -} - -// virtual -void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask) -{ - if (mMouseEnterSignal) - { - (*mMouseEnterSignal)(this, getValue()); - } -} - -// virtual -void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask) -{ - if(mMouseLeaveSignal) - { - (*mMouseLeaveSignal)(this, getValue()); - } -} - -//virtual -bool LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) -{ - - LL_DEBUGS() << "LLUICtrl::handleMouseDown calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; - - bool handled = LLView::handleMouseDown(x,y,mask); - - if (mMouseDownSignal) - { - (*mMouseDownSignal)(this,x,y,mask); - } - LL_DEBUGS() << "LLUICtrl::handleMousedown - handled is returning as: " << handled << " " << LL_ENDL; - - if (handled) { - LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname()); - } - return handled; -} - -//virtual -bool LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) -{ - - LL_DEBUGS() << "LLUICtrl::handleMouseUp calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; - - bool handled = LLView::handleMouseUp(x,y,mask); - if (handled) { - LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname()); - } - if (mMouseUpSignal) - { - (*mMouseUpSignal)(this,x,y,mask); - } - - LL_DEBUGS() << "LLUICtrl::handleMouseUp - handled for xui " << getPathname() << " - is returning as: " << handled << " " << LL_ENDL; - - return handled; -} - -//virtual -bool LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = LLView::handleRightMouseDown(x,y,mask); - if (mRightMouseDownSignal) - { - (*mRightMouseDownSignal)(this,x,y,mask); - } - return handled; -} - -//virtual -bool LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = LLView::handleRightMouseUp(x,y,mask); - if(mRightMouseUpSignal) - { - (*mRightMouseUpSignal)(this,x,y,mask); - } - return handled; -} - -bool LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - bool handled = LLView::handleDoubleClick(x, y, mask); - if (mDoubleClickSignal) - { - (*mDoubleClickSignal)(this, x, y, mask); - } - return handled; -} - -// can't tab to children of a non-tab-stop widget -bool LLUICtrl::canFocusChildren() const -{ - return hasTabStop(); -} - - -void LLUICtrl::onCommit() -{ - if (mCommitSignal) - { - if (!mFunctionName.empty()) - { - LL_DEBUGS("UIUsage") << "calling commit function " << mFunctionName << LL_ENDL; - LLUIUsage::instance().logCommand(mFunctionName); - LLUIUsage::instance().logControl(getPathname()); - } - else - { - //LL_DEBUGS("UIUsage") << "calling commit function " << "UNKNOWN" << LL_ENDL; - } - (*mCommitSignal)(this, getValue()); - } -} - -//virtual -bool LLUICtrl::isCtrl() const -{ - return true; -} - -//virtual -void LLUICtrl::setValue(const LLSD& value) -{ - mViewModel->setValue(value); -} - -//virtual -LLSD LLUICtrl::getValue() const -{ - 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::postBuild() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - // - // Find all of the children that want to be in front and move them to the front - // - - if (getChildCount() > 0) - { - std::vector childrenToMoveToFront; - - for (LLView::child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it) - { - LLUICtrl* uictrl = dynamic_cast(*child_it); - - if (uictrl && uictrl->mRequestsFront) - { - childrenToMoveToFront.push_back(uictrl); - } - } - - for (std::vector::iterator it = childrenToMoveToFront.begin(); it != childrenToMoveToFront.end(); ++it) - { - sendChildToFront(*it); - } - } - - return LLView::postBuild(); -} - -bool LLUICtrl::setControlValue(const LLSD& value) -{ - if (mControlVariable) - { - mControlVariable->set(value); - return true; - } - return false; -} - -void LLUICtrl::setControlVariable(LLControlVariable* control) -{ - if (mControlVariable) - { - //RN: this will happen in practice, should we try to avoid it? - //LL_WARNS() << "setControlName called twice on same control!" << LL_ENDL; - mControlConnection.disconnect(); // disconnect current signal - mControlVariable = NULL; - } - - if (control) - { - mControlVariable = control; - mControlConnection = mControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("value"))); - setValue(mControlVariable->getValue()); - } -} - -void LLUICtrl::removeControlVariable() -{ - if (mControlVariable) - { - mControlConnection.disconnect(); - mControlVariable = NULL; - } -} - -//virtual -void LLUICtrl::setControlName(const std::string& control_name, LLView *context) -{ - if (context == NULL) - { - context = this; - } - - // Register new listener - if (!control_name.empty()) - { - LLControlVariable* control = context->findControl(control_name); - if (!control) - { - LL_WARNS() << "Failed to assign control variable to " << getName() - << ": control "<< control_name << " does not exist." << LL_ENDL; - } - setControlVariable(control); - } -} - -void LLUICtrl::setEnabledControlVariable(LLControlVariable* control) -{ - if (mEnabledControlVariable) - { - mEnabledControlConnection.disconnect(); // disconnect current signal - mEnabledControlVariable = NULL; - } - if (control) - { - mEnabledControlVariable = control; - mEnabledControlConnection = mEnabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("enabled"))); - setEnabled(mEnabledControlVariable->getValue().asBoolean()); - } -} - -void LLUICtrl::setDisabledControlVariable(LLControlVariable* control) -{ - if (mDisabledControlVariable) - { - mDisabledControlConnection.disconnect(); // disconnect current signal - mDisabledControlVariable = NULL; - } - if (control) - { - mDisabledControlVariable = control; - mDisabledControlConnection = mDisabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("disabled"))); - setEnabled(!(mDisabledControlVariable->getValue().asBoolean())); - } -} - -void LLUICtrl::setMakeVisibleControlVariable(LLControlVariable* control) -{ - if (mMakeVisibleControlVariable) - { - mMakeVisibleControlConnection.disconnect(); // disconnect current signal - mMakeVisibleControlVariable = NULL; - } - if (control) - { - mMakeVisibleControlVariable = control; - mMakeVisibleControlConnection = mMakeVisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("visible"))); - setVisible(mMakeVisibleControlVariable->getValue().asBoolean()); - } -} - -void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control) -{ - if (mMakeInvisibleControlVariable) - { - mMakeInvisibleControlConnection.disconnect(); // disconnect current signal - mMakeInvisibleControlVariable = NULL; - } - if (control) - { - mMakeInvisibleControlVariable = control; - mMakeInvisibleControlConnection = mMakeInvisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("invisible"))); - setVisible(!(mMakeInvisibleControlVariable->getValue().asBoolean())); - } -} - -void LLUICtrl::setFunctionName(const std::string& function_name) -{ - mFunctionName = function_name; -} - -// static -bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle handle, std::string type) -{ - LLUICtrl* ctrl = handle.get(); - if (ctrl) - { - if (type == "value") - { - ctrl->setValue(newvalue); - return true; - } - else if (type == "enabled") - { - ctrl->setEnabled(newvalue.asBoolean()); - return true; - } - else if(type =="disabled") - { - ctrl->setEnabled(!newvalue.asBoolean()); - return true; - } - else if (type == "visible") - { - ctrl->setVisible(newvalue.asBoolean()); - return true; - } - else if (type == "invisible") - { - ctrl->setVisible(!newvalue.asBoolean()); - return true; - } - } - return false; -} - -// virtual -bool LLUICtrl::setTextArg( const std::string& key, const LLStringExplicit& text ) -{ - return false; -} - -// virtual -bool LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - return false; -} - -// virtual -LLCtrlSelectionInterface* LLUICtrl::getSelectionInterface() -{ - return NULL; -} - -// virtual -LLCtrlListInterface* LLUICtrl::getListInterface() -{ - return NULL; -} - -// virtual -LLCtrlScrollInterface* LLUICtrl::getScrollInterface() -{ - return NULL; -} - -bool LLUICtrl::hasFocus() const -{ - return (gFocusMgr.childHasKeyboardFocus(this)); -} - -void LLUICtrl::setFocus(bool b) -{ - // focus NEVER goes to ui ctrls that are disabled! - if (!getEnabled()) - { - return; - } - if( b ) - { - if (!hasFocus()) - { - gFocusMgr.setKeyboardFocus( this ); - } - } - else - { - if( gFocusMgr.childHasKeyboardFocus(this)) - { - gFocusMgr.setKeyboardFocus( NULL ); - } - } -} - -// virtual -void LLUICtrl::setTabStop( bool b ) -{ - mTabStop = b; -} - -// virtual -bool LLUICtrl::hasTabStop() const -{ - return mTabStop; -} - -// virtual -bool LLUICtrl::acceptsTextInput() const -{ - return false; -} - -//virtual -bool LLUICtrl::isDirty() const -{ - return mViewModel->isDirty(); -}; - -//virtual -void LLUICtrl::resetDirty() -{ - mViewModel->resetDirty(); -} - -// virtual -void LLUICtrl::onTabInto() -{ - onUpdateScrollToChild(this); -} - -// virtual -void LLUICtrl::clear() -{ -} - -// virtual -void LLUICtrl::setIsChrome(bool is_chrome) -{ - mIsChrome = is_chrome; -} - -// virtual -bool LLUICtrl::getIsChrome() const -{ - if (mIsChrome) - return true; - - LLView* parent_ctrl = getParent(); - while (parent_ctrl) - { - if (parent_ctrl->isCtrl()) - return ((LLUICtrl*)parent_ctrl)->getIsChrome(); - - parent_ctrl = parent_ctrl->getParent(); - } - - return false; -} - - -bool LLUICtrl::focusFirstItem(bool prefer_text_fields, bool focus_flash) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - // try to select default tab group child - LLViewQuery query = getTabOrderQuery(); - child_list_t result = query(this); - if(result.size() > 0) - { - LLUICtrl * ctrl = static_cast(result.back()); - if(!ctrl->hasFocus()) - { - ctrl->setFocus(true); - ctrl->onTabInto(); - if(focus_flash) - { - gFocusMgr.triggerFocusFlash(); - } - } - return true; - } - // search for text field first - if(prefer_text_fields) - { - LLViewQuery query = getTabOrderQuery(); - query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); - child_list_t result = query(this); - if(result.size() > 0) - { - LLUICtrl * ctrl = static_cast(result.back()); - if(!ctrl->hasFocus()) - { - ctrl->setFocus(true); - ctrl->onTabInto(); - if(focus_flash) - { - gFocusMgr.triggerFocusFlash(); - } - } - return true; - } - } - // no text field found, or we don't care about text fields - result = getTabOrderQuery().run(this); - if(result.size() > 0) - { - LLUICtrl * ctrl = static_cast(result.back()); - if(!ctrl->hasFocus()) - { - ctrl->setFocus(true); - ctrl->onTabInto(); - if(focus_flash) - { - gFocusMgr.triggerFocusFlash(); - } - } - return true; - } - return false; -} - - -bool LLUICtrl::focusNextItem(bool text_fields_only) -{ - // this assumes that this method is called on the focus root. - LLViewQuery query = getTabOrderQuery(); - static LLUICachedControl tab_to_text_fields_only ("TabToTextFieldsOnly", false); - if(text_fields_only || tab_to_text_fields_only) - { - query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); - } - child_list_t result = query(this); - return focusNext(result); -} - -bool LLUICtrl::focusPrevItem(bool text_fields_only) -{ - // this assumes that this method is called on the focus root. - LLViewQuery query = getTabOrderQuery(); - static LLUICachedControl tab_to_text_fields_only ("TabToTextFieldsOnly", false); - if(text_fields_only || tab_to_text_fields_only) - { - query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); - } - child_list_t result = query(this); - return focusPrev(result); -} - -LLUICtrl* LLUICtrl::findRootMostFocusRoot() -{ - LLUICtrl* focus_root = NULL; - LLUICtrl* next_view = this; - while(next_view && next_view->hasTabStop()) - { - if (next_view->isFocusRoot()) - { - focus_root = next_view; - } - next_view = next_view->getParentUICtrl(); - } - - return focus_root; -} - -// Skip over any parents that are not LLUICtrl's -// Used in focus logic since only LLUICtrl elements can have focus -LLUICtrl* LLUICtrl::getParentUICtrl() const -{ - LLView* parent = getParent(); - while (parent) - { - if (parent->isCtrl()) - { - return (LLUICtrl*)(parent); - } - else - { - parent = parent->getParent(); - } - } - return NULL; -} - -bool LLUICtrl::findHelpTopic(std::string& help_topic_out) -{ - LLUICtrl* ctrl = this; - - // search back through the control's parents for a panel - // or tab with a help_topic string defined - while (ctrl) - { - LLPanel *panel = dynamic_cast(ctrl); - - if (panel) - { - - LLView *child; - LLPanel *subpanel = NULL; - - // does the panel have a sub-panel with a help topic? - bfs_tree_iterator_t it = beginTreeBFS(); - // skip ourselves - ++it; - for (; it != endTreeBFS(); ++it) - { - child = *it; - // do we have a panel with a help topic? - LLPanel *panel = dynamic_cast(child); - if (panel && panel->isInVisibleChain() && !panel->getHelpTopic().empty()) - { - subpanel = panel; - break; - } - } - - if (subpanel) - { - help_topic_out = subpanel->getHelpTopic(); - return true; // success (subpanel) - } - - // does the panel have an active tab with a help topic? - LLPanel *tab_panel = NULL; - - it = beginTreeBFS(); - // skip ourselves - ++it; - for (; it != endTreeBFS(); ++it) - { - child = *it; - LLPanel *curTabPanel = NULL; - - // do we have a tab container? - LLTabContainer *tab = dynamic_cast(child); - if (tab && tab->getVisible()) - { - curTabPanel = tab->getCurrentPanel(); - } - - // do we have an accordion tab? - LLAccordionCtrlTab* accordion = dynamic_cast(child); - if (accordion && accordion->getDisplayChildren()) - { - curTabPanel = dynamic_cast(accordion->getAccordionView()); - } - - // if we found a valid tab, does it have a help topic? - if (curTabPanel && !curTabPanel->getHelpTopic().empty()) - { - tab_panel = curTabPanel; - break; - } - } - - if (tab_panel) - { - help_topic_out = tab_panel->getHelpTopic(); - return true; // success (tab) - } - - // otherwise, does the panel have a help topic itself? - if (!panel->getHelpTopic().empty()) - { - help_topic_out = panel->getHelpTopic(); - return true; // success (panel) - } - } - - ctrl = ctrl->getParentUICtrl(); - } - - return false; // no help topic found -} - -// *TODO: Deprecate; for backwards compatability only: -boost::signals2::connection LLUICtrl::setCommitCallback( boost::function cb, void* data) -{ - return setCommitCallback( boost::bind(cb, _1, data)); -} -boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function cb ) -{ - if (!mValidateSignal) mValidateSignal = new enable_signal_t(); - - return mValidateSignal->connect(boost::bind(cb, _2)); -} - -// virtual -void LLUICtrl::setTentative(bool b) -{ - mTentative = b; -} - -// virtual -bool LLUICtrl::getTentative() const -{ - return mTentative; -} - -// virtual -void LLUICtrl::setColor(const LLColor4& color) -{ } - -F32 LLUICtrl::getCurrentTransparency() -{ - F32 alpha = 0; - - switch(mTransparencyType) - { - case TT_DEFAULT: - alpha = getDrawContext().mAlpha; - break; - - case TT_ACTIVE: - alpha = sActiveControlTransparency; - break; - - case TT_INACTIVE: - alpha = sInactiveControlTransparency; - break; - - case TT_FADING: - alpha = sInactiveControlTransparency / 2; - break; - } - - return alpha; -} - -void LLUICtrl::setTransparencyType(ETypeTransparency type) -{ - mTransparencyType = type; -} - -boost::signals2::connection LLUICtrl::setCommitCallback(const CommitCallbackParam& cb) -{ - return setCommitCallback(initCommitCallback(cb)); -} - -boost::signals2::connection LLUICtrl::setValidateCallback(const EnableCallbackParam& cb) -{ - return setValidateCallback(initEnableCallback(cb)); -} - -boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mCommitSignal) mCommitSignal = new commit_signal_t(); - - return mCommitSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t::slot_type& cb ) -{ - if (!mValidateSignal) mValidateSignal = new enable_signal_t(); - - return mValidateSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setMouseEnterCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mMouseEnterSignal) mMouseEnterSignal = new commit_signal_t(); - - return mMouseEnterSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t(); - - return mMouseLeaveSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setMouseDownCallback( const mouse_signal_t::slot_type& cb ) -{ - if (!mMouseDownSignal) mMouseDownSignal = new mouse_signal_t(); - - return mMouseDownSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setMouseUpCallback( const mouse_signal_t::slot_type& cb ) -{ - if (!mMouseUpSignal) mMouseUpSignal = new mouse_signal_t(); - - return mMouseUpSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setRightMouseDownCallback( const mouse_signal_t::slot_type& cb ) -{ - if (!mRightMouseDownSignal) mRightMouseDownSignal = new mouse_signal_t(); - - return mRightMouseDownSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setRightMouseUpCallback( const mouse_signal_t::slot_type& cb ) -{ - if (!mRightMouseUpSignal) mRightMouseUpSignal = new mouse_signal_t(); - - return mRightMouseUpSignal->connect(cb); -} - -boost::signals2::connection LLUICtrl::setDoubleClickCallback( const mouse_signal_t::slot_type& cb ) -{ - if (!mDoubleClickSignal) mDoubleClickSignal = new mouse_signal_t(); - - return mDoubleClickSignal->connect(cb); -} - -void LLUICtrl::addInfo(LLSD & info) -{ - LLView::addInfo(info); - info["value"] = getValue(); -} +/** + * @file lluictrl.cpp + * @author James Cook, Richard Nelson, Tom Yedwab + * @brief Abstract base class for UI controls + * + * $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 "linden_common.h" + +#define LLUICTRL_CPP +#include "lluictrl.h" +#include "llviewereventrecorder.h" +#include "llfocusmgr.h" +#include "llpanel.h" +#include "lluictrlfactory.h" +#include "lltabcontainer.h" +#include "llaccordionctrltab.h" +#include "lluiusage.h" + +static LLDefaultChildRegistry::Register r("ui_ctrl"); + +F32 LLUICtrl::sActiveControlTransparency = 1.0f; +F32 LLUICtrl::sInactiveControlTransparency = 1.0f; + +// Compiler optimization, generate extern template +template class LLUICtrl* LLView::getChild( + const std::string& name, bool recurse) const; + +LLUICtrl::CallbackParam::CallbackParam() +: name("name"), + function_name("function"), + parameter("parameter"), + control_name("control") // Shortcut to control -> "control_name" for backwards compatability +{ + addSynonym(parameter, "userdata"); +} + +LLUICtrl::EnableControls::EnableControls() +: enabled("enabled_control"), + disabled("disabled_control") +{} + +LLUICtrl::ControlVisibility::ControlVisibility() +: visible("visibility_control"), + invisible("invisibility_control") +{ + addSynonym(visible, "visiblity_control"); + addSynonym(invisible, "invisiblity_control"); +} + +LLUICtrl::Params::Params() +: tab_stop("tab_stop", true), + chrome("chrome", false), + requests_front("requests_front", false), + label("label"), + initial_value("value"), + init_callback("init_callback"), + commit_callback("commit_callback"), + validate_callback("validate_callback"), + mouseenter_callback("mouseenter_callback"), + mouseleave_callback("mouseleave_callback"), + control_name("control_name"), + font("font", LLFontGL::getFontEmojiMedium()), + font_halign("halign"), + font_valign("valign"), + length("length"), // ignore LLXMLNode cruft + type("type") // ignore LLXMLNode cruft +{ + addSynonym(initial_value, "initial_value"); +} + +// NOTE: the LLFocusableElement implementation has been moved from here to llfocusmgr.cpp. + +//static +const LLUICtrl::Params& LLUICtrl::getDefaultParams() +{ + return LLUICtrlFactory::getDefaultParams(); +} + + +LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) +: LLView(p), + mIsChrome(false), + mRequestsFront(p.requests_front), + mTabStop(false), + mTentative(false), + mViewModel(viewmodel), + mControlVariable(NULL), + mEnabledControlVariable(NULL), + mDisabledControlVariable(NULL), + mMakeVisibleControlVariable(NULL), + mMakeInvisibleControlVariable(NULL), + mCommitSignal(NULL), + mValidateSignal(NULL), + mMouseEnterSignal(NULL), + mMouseLeaveSignal(NULL), + mMouseDownSignal(NULL), + mMouseUpSignal(NULL), + mRightMouseDownSignal(NULL), + mRightMouseUpSignal(NULL), + mDoubleClickSignal(NULL), + mTransparencyType(TT_DEFAULT) +{ +} + +void LLUICtrl::initFromParams(const Params& p) +{ + LLView::initFromParams(p); + + mRequestsFront = p.requests_front; + + setIsChrome(p.chrome); + setControlName(p.control_name); + if(p.enabled_controls.isProvided()) + { + if (p.enabled_controls.enabled.isChosen()) + { + LLControlVariable* control = findControl(p.enabled_controls.enabled); + if (control) + { + setEnabledControlVariable(control); + } + else + { + LL_WARNS() << "Failed to assign 'enabled' control variable to " << getName() + << ": control " << p.enabled_controls.enabled() + << " does not exist." << LL_ENDL; + } + } + else if(p.enabled_controls.disabled.isChosen()) + { + LLControlVariable* control = findControl(p.enabled_controls.disabled); + if (control) + { + setDisabledControlVariable(control); + } + else + { + LL_WARNS() << "Failed to assign 'disabled' control variable to " << getName() + << ": control " << p.enabled_controls.disabled() + << " does not exist." << LL_ENDL; + } + } + } + if(p.controls_visibility.isProvided()) + { + if (p.controls_visibility.visible.isChosen()) + { + LLControlVariable* control = findControl(p.controls_visibility.visible); + if (control) + { + setMakeVisibleControlVariable(control); + } + else + { + LL_WARNS() << "Failed to assign visibility control variable to " << getName() + << ": control " << p.controls_visibility.visible() + << " does not exist." << LL_ENDL; + } + } + else if (p.controls_visibility.invisible.isChosen()) + { + LLControlVariable* control = findControl(p.controls_visibility.invisible); + if (control) + { + setMakeInvisibleControlVariable(control); + } + else + { + LL_WARNS() << "Failed to assign invisibility control variable to " << getName() + << ": control " << p.controls_visibility.invisible() + << " does not exist." << LL_ENDL; + } + } + } + + setTabStop(p.tab_stop); + + if (p.initial_value.isProvided() + && !p.control_name.isProvided()) + { + setValue(p.initial_value); + } + + if (p.commit_callback.isProvided()) + { + setCommitCallback(initCommitCallback(p.commit_callback)); + } + + if (p.validate_callback.isProvided()) + { + setValidateCallback(initEnableCallback(p.validate_callback)); + } + + if (p.init_callback.isProvided()) + { + if (p.init_callback.function.isProvided()) + { + p.init_callback.function()(this, p.init_callback.parameter); + } + else + { + commit_callback_t* initfunc = (CommitCallbackRegistry::getValue(p.init_callback.function_name)); + if (initfunc) + { + (*initfunc)(this, p.init_callback.parameter); + } + } + } + + if(p.mouseenter_callback.isProvided()) + { + setMouseEnterCallback(initCommitCallback(p.mouseenter_callback)); + } + + if(p.mouseleave_callback.isProvided()) + { + setMouseLeaveCallback(initCommitCallback(p.mouseleave_callback)); + } +} + + +LLUICtrl::~LLUICtrl() +{ + gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() + + if( gFocusMgr.getTopCtrl() == this ) + { + LL_WARNS() << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << LL_ENDL; + gFocusMgr.removeTopCtrlWithoutCallback( this ); + } + + delete mCommitSignal; + delete mValidateSignal; + delete mMouseEnterSignal; + delete mMouseLeaveSignal; + delete mMouseDownSignal; + delete mMouseUpSignal; + delete mRightMouseDownSignal; + delete mRightMouseUpSignal; + delete mDoubleClickSignal; +} + +void default_commit_handler(LLUICtrl* ctrl, const LLSD& param) +{} + +bool default_enable_handler(LLUICtrl* ctrl, const LLSD& param) +{ + return true; +} + + +LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCallbackParam& cb) +{ + if (cb.function.isProvided()) + { + if (cb.parameter.isProvided()) + return boost::bind(cb.function(), _1, cb.parameter); + else + return cb.function(); + } + else + { + std::string function_name = cb.function_name; + setFunctionName(function_name); + commit_callback_t* func = (CommitCallbackRegistry::getValue(function_name)); + if (func) + { + if (cb.parameter.isProvided()) + return boost::bind((*func), _1, cb.parameter); + else + return commit_signal_t::slot_type(*func); + } + else if (!function_name.empty()) + { + LL_WARNS() << "No callback found for: '" << function_name << "' in control: " << getName() << LL_ENDL; + } + } + return default_commit_handler; +} + +LLUICtrl::enable_signal_t::slot_type LLUICtrl::initEnableCallback(const EnableCallbackParam& cb) +{ + // Set the callback function + if (cb.function.isProvided()) + { + if (cb.parameter.isProvided()) + return boost::bind(cb.function(), this, cb.parameter); + else + return cb.function(); + } + else + { + enable_callback_t* func = (EnableCallbackRegistry::getValue(cb.function_name)); + if (func) + { + if (cb.parameter.isProvided()) + return boost::bind((*func), this, cb.parameter); + else + return enable_signal_t::slot_type(*func); + } + } + return default_enable_handler; +} + +// virtual +void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask) +{ + if (mMouseEnterSignal) + { + (*mMouseEnterSignal)(this, getValue()); + } +} + +// virtual +void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask) +{ + if(mMouseLeaveSignal) + { + (*mMouseLeaveSignal)(this, getValue()); + } +} + +//virtual +bool LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) +{ + + LL_DEBUGS() << "LLUICtrl::handleMouseDown calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; + + bool handled = LLView::handleMouseDown(x,y,mask); + + if (mMouseDownSignal) + { + (*mMouseDownSignal)(this,x,y,mask); + } + LL_DEBUGS() << "LLUICtrl::handleMousedown - handled is returning as: " << handled << " " << LL_ENDL; + + if (handled) { + LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname()); + } + return handled; +} + +//virtual +bool LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + + LL_DEBUGS() << "LLUICtrl::handleMouseUp calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL; + + bool handled = LLView::handleMouseUp(x,y,mask); + if (handled) { + LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname()); + } + if (mMouseUpSignal) + { + (*mMouseUpSignal)(this,x,y,mask); + } + + LL_DEBUGS() << "LLUICtrl::handleMouseUp - handled for xui " << getPathname() << " - is returning as: " << handled << " " << LL_ENDL; + + return handled; +} + +//virtual +bool LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = LLView::handleRightMouseDown(x,y,mask); + if (mRightMouseDownSignal) + { + (*mRightMouseDownSignal)(this,x,y,mask); + } + return handled; +} + +//virtual +bool LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = LLView::handleRightMouseUp(x,y,mask); + if(mRightMouseUpSignal) + { + (*mRightMouseUpSignal)(this,x,y,mask); + } + return handled; +} + +bool LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + bool handled = LLView::handleDoubleClick(x, y, mask); + if (mDoubleClickSignal) + { + (*mDoubleClickSignal)(this, x, y, mask); + } + return handled; +} + +// can't tab to children of a non-tab-stop widget +bool LLUICtrl::canFocusChildren() const +{ + return hasTabStop(); +} + + +void LLUICtrl::onCommit() +{ + if (mCommitSignal) + { + if (!mFunctionName.empty()) + { + LL_DEBUGS("UIUsage") << "calling commit function " << mFunctionName << LL_ENDL; + LLUIUsage::instance().logCommand(mFunctionName); + LLUIUsage::instance().logControl(getPathname()); + } + else + { + //LL_DEBUGS("UIUsage") << "calling commit function " << "UNKNOWN" << LL_ENDL; + } + (*mCommitSignal)(this, getValue()); + } +} + +//virtual +bool LLUICtrl::isCtrl() const +{ + return true; +} + +//virtual +void LLUICtrl::setValue(const LLSD& value) +{ + mViewModel->setValue(value); +} + +//virtual +LLSD LLUICtrl::getValue() const +{ + 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::postBuild() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + // + // Find all of the children that want to be in front and move them to the front + // + + if (getChildCount() > 0) + { + std::vector childrenToMoveToFront; + + for (LLView::child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it) + { + LLUICtrl* uictrl = dynamic_cast(*child_it); + + if (uictrl && uictrl->mRequestsFront) + { + childrenToMoveToFront.push_back(uictrl); + } + } + + for (std::vector::iterator it = childrenToMoveToFront.begin(); it != childrenToMoveToFront.end(); ++it) + { + sendChildToFront(*it); + } + } + + return LLView::postBuild(); +} + +bool LLUICtrl::setControlValue(const LLSD& value) +{ + if (mControlVariable) + { + mControlVariable->set(value); + return true; + } + return false; +} + +void LLUICtrl::setControlVariable(LLControlVariable* control) +{ + if (mControlVariable) + { + //RN: this will happen in practice, should we try to avoid it? + //LL_WARNS() << "setControlName called twice on same control!" << LL_ENDL; + mControlConnection.disconnect(); // disconnect current signal + mControlVariable = NULL; + } + + if (control) + { + mControlVariable = control; + mControlConnection = mControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("value"))); + setValue(mControlVariable->getValue()); + } +} + +void LLUICtrl::removeControlVariable() +{ + if (mControlVariable) + { + mControlConnection.disconnect(); + mControlVariable = NULL; + } +} + +//virtual +void LLUICtrl::setControlName(const std::string& control_name, LLView *context) +{ + if (context == NULL) + { + context = this; + } + + // Register new listener + if (!control_name.empty()) + { + LLControlVariable* control = context->findControl(control_name); + if (!control) + { + LL_WARNS() << "Failed to assign control variable to " << getName() + << ": control "<< control_name << " does not exist." << LL_ENDL; + } + setControlVariable(control); + } +} + +void LLUICtrl::setEnabledControlVariable(LLControlVariable* control) +{ + if (mEnabledControlVariable) + { + mEnabledControlConnection.disconnect(); // disconnect current signal + mEnabledControlVariable = NULL; + } + if (control) + { + mEnabledControlVariable = control; + mEnabledControlConnection = mEnabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("enabled"))); + setEnabled(mEnabledControlVariable->getValue().asBoolean()); + } +} + +void LLUICtrl::setDisabledControlVariable(LLControlVariable* control) +{ + if (mDisabledControlVariable) + { + mDisabledControlConnection.disconnect(); // disconnect current signal + mDisabledControlVariable = NULL; + } + if (control) + { + mDisabledControlVariable = control; + mDisabledControlConnection = mDisabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("disabled"))); + setEnabled(!(mDisabledControlVariable->getValue().asBoolean())); + } +} + +void LLUICtrl::setMakeVisibleControlVariable(LLControlVariable* control) +{ + if (mMakeVisibleControlVariable) + { + mMakeVisibleControlConnection.disconnect(); // disconnect current signal + mMakeVisibleControlVariable = NULL; + } + if (control) + { + mMakeVisibleControlVariable = control; + mMakeVisibleControlConnection = mMakeVisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("visible"))); + setVisible(mMakeVisibleControlVariable->getValue().asBoolean()); + } +} + +void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control) +{ + if (mMakeInvisibleControlVariable) + { + mMakeInvisibleControlConnection.disconnect(); // disconnect current signal + mMakeInvisibleControlVariable = NULL; + } + if (control) + { + mMakeInvisibleControlVariable = control; + mMakeInvisibleControlConnection = mMakeInvisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("invisible"))); + setVisible(!(mMakeInvisibleControlVariable->getValue().asBoolean())); + } +} + +void LLUICtrl::setFunctionName(const std::string& function_name) +{ + mFunctionName = function_name; +} + +// static +bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle handle, std::string type) +{ + LLUICtrl* ctrl = handle.get(); + if (ctrl) + { + if (type == "value") + { + ctrl->setValue(newvalue); + return true; + } + else if (type == "enabled") + { + ctrl->setEnabled(newvalue.asBoolean()); + return true; + } + else if(type =="disabled") + { + ctrl->setEnabled(!newvalue.asBoolean()); + return true; + } + else if (type == "visible") + { + ctrl->setVisible(newvalue.asBoolean()); + return true; + } + else if (type == "invisible") + { + ctrl->setVisible(!newvalue.asBoolean()); + return true; + } + } + return false; +} + +// virtual +bool LLUICtrl::setTextArg( const std::string& key, const LLStringExplicit& text ) +{ + return false; +} + +// virtual +bool LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + return false; +} + +// virtual +LLCtrlSelectionInterface* LLUICtrl::getSelectionInterface() +{ + return NULL; +} + +// virtual +LLCtrlListInterface* LLUICtrl::getListInterface() +{ + return NULL; +} + +// virtual +LLCtrlScrollInterface* LLUICtrl::getScrollInterface() +{ + return NULL; +} + +bool LLUICtrl::hasFocus() const +{ + return (gFocusMgr.childHasKeyboardFocus(this)); +} + +void LLUICtrl::setFocus(bool b) +{ + // focus NEVER goes to ui ctrls that are disabled! + if (!getEnabled()) + { + return; + } + if( b ) + { + if (!hasFocus()) + { + gFocusMgr.setKeyboardFocus( this ); + } + } + else + { + if( gFocusMgr.childHasKeyboardFocus(this)) + { + gFocusMgr.setKeyboardFocus( NULL ); + } + } +} + +// virtual +void LLUICtrl::setTabStop( bool b ) +{ + mTabStop = b; +} + +// virtual +bool LLUICtrl::hasTabStop() const +{ + return mTabStop; +} + +// virtual +bool LLUICtrl::acceptsTextInput() const +{ + return false; +} + +//virtual +bool LLUICtrl::isDirty() const +{ + return mViewModel->isDirty(); +}; + +//virtual +void LLUICtrl::resetDirty() +{ + mViewModel->resetDirty(); +} + +// virtual +void LLUICtrl::onTabInto() +{ + onUpdateScrollToChild(this); +} + +// virtual +void LLUICtrl::clear() +{ +} + +// virtual +void LLUICtrl::setIsChrome(bool is_chrome) +{ + mIsChrome = is_chrome; +} + +// virtual +bool LLUICtrl::getIsChrome() const +{ + if (mIsChrome) + return true; + + LLView* parent_ctrl = getParent(); + while (parent_ctrl) + { + if (parent_ctrl->isCtrl()) + return ((LLUICtrl*)parent_ctrl)->getIsChrome(); + + parent_ctrl = parent_ctrl->getParent(); + } + + return false; +} + + +bool LLUICtrl::focusFirstItem(bool prefer_text_fields, bool focus_flash) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + // try to select default tab group child + LLViewQuery query = getTabOrderQuery(); + child_list_t result = query(this); + if(result.size() > 0) + { + LLUICtrl * ctrl = static_cast(result.back()); + if(!ctrl->hasFocus()) + { + ctrl->setFocus(true); + ctrl->onTabInto(); + if(focus_flash) + { + gFocusMgr.triggerFocusFlash(); + } + } + return true; + } + // search for text field first + if(prefer_text_fields) + { + LLViewQuery query = getTabOrderQuery(); + query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); + child_list_t result = query(this); + if(result.size() > 0) + { + LLUICtrl * ctrl = static_cast(result.back()); + if(!ctrl->hasFocus()) + { + ctrl->setFocus(true); + ctrl->onTabInto(); + if(focus_flash) + { + gFocusMgr.triggerFocusFlash(); + } + } + return true; + } + } + // no text field found, or we don't care about text fields + result = getTabOrderQuery().run(this); + if(result.size() > 0) + { + LLUICtrl * ctrl = static_cast(result.back()); + if(!ctrl->hasFocus()) + { + ctrl->setFocus(true); + ctrl->onTabInto(); + if(focus_flash) + { + gFocusMgr.triggerFocusFlash(); + } + } + return true; + } + return false; +} + + +bool LLUICtrl::focusNextItem(bool text_fields_only) +{ + // this assumes that this method is called on the focus root. + LLViewQuery query = getTabOrderQuery(); + static LLUICachedControl tab_to_text_fields_only ("TabToTextFieldsOnly", false); + if(text_fields_only || tab_to_text_fields_only) + { + query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); + } + child_list_t result = query(this); + return focusNext(result); +} + +bool LLUICtrl::focusPrevItem(bool text_fields_only) +{ + // this assumes that this method is called on the focus root. + LLViewQuery query = getTabOrderQuery(); + static LLUICachedControl tab_to_text_fields_only ("TabToTextFieldsOnly", false); + if(text_fields_only || tab_to_text_fields_only) + { + query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); + } + child_list_t result = query(this); + return focusPrev(result); +} + +LLUICtrl* LLUICtrl::findRootMostFocusRoot() +{ + LLUICtrl* focus_root = NULL; + LLUICtrl* next_view = this; + while(next_view && next_view->hasTabStop()) + { + if (next_view->isFocusRoot()) + { + focus_root = next_view; + } + next_view = next_view->getParentUICtrl(); + } + + return focus_root; +} + +// Skip over any parents that are not LLUICtrl's +// Used in focus logic since only LLUICtrl elements can have focus +LLUICtrl* LLUICtrl::getParentUICtrl() const +{ + LLView* parent = getParent(); + while (parent) + { + if (parent->isCtrl()) + { + return (LLUICtrl*)(parent); + } + else + { + parent = parent->getParent(); + } + } + return NULL; +} + +bool LLUICtrl::findHelpTopic(std::string& help_topic_out) +{ + LLUICtrl* ctrl = this; + + // search back through the control's parents for a panel + // or tab with a help_topic string defined + while (ctrl) + { + LLPanel *panel = dynamic_cast(ctrl); + + if (panel) + { + + LLView *child; + LLPanel *subpanel = NULL; + + // does the panel have a sub-panel with a help topic? + bfs_tree_iterator_t it = beginTreeBFS(); + // skip ourselves + ++it; + for (; it != endTreeBFS(); ++it) + { + child = *it; + // do we have a panel with a help topic? + LLPanel *panel = dynamic_cast(child); + if (panel && panel->isInVisibleChain() && !panel->getHelpTopic().empty()) + { + subpanel = panel; + break; + } + } + + if (subpanel) + { + help_topic_out = subpanel->getHelpTopic(); + return true; // success (subpanel) + } + + // does the panel have an active tab with a help topic? + LLPanel *tab_panel = NULL; + + it = beginTreeBFS(); + // skip ourselves + ++it; + for (; it != endTreeBFS(); ++it) + { + child = *it; + LLPanel *curTabPanel = NULL; + + // do we have a tab container? + LLTabContainer *tab = dynamic_cast(child); + if (tab && tab->getVisible()) + { + curTabPanel = tab->getCurrentPanel(); + } + + // do we have an accordion tab? + LLAccordionCtrlTab* accordion = dynamic_cast(child); + if (accordion && accordion->getDisplayChildren()) + { + curTabPanel = dynamic_cast(accordion->getAccordionView()); + } + + // if we found a valid tab, does it have a help topic? + if (curTabPanel && !curTabPanel->getHelpTopic().empty()) + { + tab_panel = curTabPanel; + break; + } + } + + if (tab_panel) + { + help_topic_out = tab_panel->getHelpTopic(); + return true; // success (tab) + } + + // otherwise, does the panel have a help topic itself? + if (!panel->getHelpTopic().empty()) + { + help_topic_out = panel->getHelpTopic(); + return true; // success (panel) + } + } + + ctrl = ctrl->getParentUICtrl(); + } + + return false; // no help topic found +} + +// *TODO: Deprecate; for backwards compatability only: +boost::signals2::connection LLUICtrl::setCommitCallback( boost::function cb, void* data) +{ + return setCommitCallback( boost::bind(cb, _1, data)); +} +boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function cb ) +{ + if (!mValidateSignal) mValidateSignal = new enable_signal_t(); + + return mValidateSignal->connect(boost::bind(cb, _2)); +} + +// virtual +void LLUICtrl::setTentative(bool b) +{ + mTentative = b; +} + +// virtual +bool LLUICtrl::getTentative() const +{ + return mTentative; +} + +// virtual +void LLUICtrl::setColor(const LLColor4& color) +{ } + +F32 LLUICtrl::getCurrentTransparency() +{ + F32 alpha = 0; + + switch(mTransparencyType) + { + case TT_DEFAULT: + alpha = getDrawContext().mAlpha; + break; + + case TT_ACTIVE: + alpha = sActiveControlTransparency; + break; + + case TT_INACTIVE: + alpha = sInactiveControlTransparency; + break; + + case TT_FADING: + alpha = sInactiveControlTransparency / 2; + break; + } + + return alpha; +} + +void LLUICtrl::setTransparencyType(ETypeTransparency type) +{ + mTransparencyType = type; +} + +boost::signals2::connection LLUICtrl::setCommitCallback(const CommitCallbackParam& cb) +{ + return setCommitCallback(initCommitCallback(cb)); +} + +boost::signals2::connection LLUICtrl::setValidateCallback(const EnableCallbackParam& cb) +{ + return setValidateCallback(initEnableCallback(cb)); +} + +boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mCommitSignal) mCommitSignal = new commit_signal_t(); + + return mCommitSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t::slot_type& cb ) +{ + if (!mValidateSignal) mValidateSignal = new enable_signal_t(); + + return mValidateSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setMouseEnterCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseEnterSignal) mMouseEnterSignal = new commit_signal_t(); + + return mMouseEnterSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t(); + + return mMouseLeaveSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setMouseDownCallback( const mouse_signal_t::slot_type& cb ) +{ + if (!mMouseDownSignal) mMouseDownSignal = new mouse_signal_t(); + + return mMouseDownSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setMouseUpCallback( const mouse_signal_t::slot_type& cb ) +{ + if (!mMouseUpSignal) mMouseUpSignal = new mouse_signal_t(); + + return mMouseUpSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setRightMouseDownCallback( const mouse_signal_t::slot_type& cb ) +{ + if (!mRightMouseDownSignal) mRightMouseDownSignal = new mouse_signal_t(); + + return mRightMouseDownSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setRightMouseUpCallback( const mouse_signal_t::slot_type& cb ) +{ + if (!mRightMouseUpSignal) mRightMouseUpSignal = new mouse_signal_t(); + + return mRightMouseUpSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setDoubleClickCallback( const mouse_signal_t::slot_type& cb ) +{ + if (!mDoubleClickSignal) mDoubleClickSignal = new mouse_signal_t(); + + return mDoubleClickSignal->connect(cb); +} + +void LLUICtrl::addInfo(LLSD & info) +{ + LLView::addInfo(info); + info["value"] = getValue(); +} diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 8dac6bb38c..c56c3c43a4 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -1,341 +1,341 @@ -/** - * @file lluictrl.h - * @author James Cook, Richard Nelson, Tom Yedwab - * @brief Abstract base class for UI controls - * - * $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$ - */ - -#ifndef LL_LLUICTRL_H -#define LL_LLUICTRL_H - -//#include "llboost.h" -#include "llrect.h" -#include "llsd.h" -#include -#include - -#include "llinitparam.h" -#include "llview.h" -#include "llviewmodel.h" // *TODO move dependency to .cpp file -#include "llsearchablecontrol.h" - -const bool TAKE_FOCUS_YES = true; -const bool TAKE_FOCUS_NO = false; -const S32 DROP_SHADOW_FLOATER = 5; - -class LLUICtrl - : public LLView, public boost::signals2::trackable -{ -public: - typedef boost::function commit_callback_t; - typedef boost::signals2::signal commit_signal_t; - // *TODO: add xml support for this type of signal in the future - typedef boost::signals2::signal mouse_signal_t; - - typedef boost::function enable_callback_t; - typedef boost::signals2::signal enable_signal_t; - - struct CallbackParam : public LLInitParam::Block - { - Ignored name; - - Optional function_name; - Optional parameter; - - Optional control_name; - - CallbackParam(); - }; - - struct CommitCallbackParam : public LLInitParam::Block - { - Optional function; - }; - - // also used for visible callbacks - struct EnableCallbackParam : public LLInitParam::Block - { - Optional function; - }; - - struct EnableControls : public LLInitParam::ChoiceBlock - { - Alternative enabled; - Alternative disabled; - - EnableControls(); - }; - struct ControlVisibility : public LLInitParam::ChoiceBlock - { - Alternative visible; - Alternative invisible; - - ControlVisibility(); - }; - struct Params : public LLInitParam::Block - { - Optional label; - Optional tab_stop, - chrome, - requests_front; - Optional initial_value; - - Optional init_callback, - commit_callback; - Optional validate_callback; - - Optional mouseenter_callback, - mouseleave_callback; - - Optional control_name; - Optional enabled_controls; - Optional controls_visibility; - - // font params - Optional font; - Optional font_halign; - Optional font_valign; - - // cruft from LLXMLNode implementation - Ignored type, - length; - - Params(); - }; - - enum ETypeTransparency - { - TT_DEFAULT, - TT_ACTIVE, // focused floater - TT_INACTIVE, // other floaters - TT_FADING, // fading toast - }; - /*virtual*/ ~LLUICtrl(); - - void initFromParams(const Params& p); -protected: - friend class LLUICtrlFactory; - static const Params& getDefaultParams(); - LLUICtrl(const Params& p = getDefaultParams(), - const LLViewModelPtr& viewmodel=LLViewModelPtr(new LLViewModel)); - - commit_signal_t::slot_type initCommitCallback(const CommitCallbackParam& cb); - enable_signal_t::slot_type initEnableCallback(const EnableCallbackParam& cb); - - // 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&); - - /*virtual*/ bool postBuild() override; - -public: - // LLView interface - /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text ) override; - /*virtual*/ bool isCtrl() const override; - /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override; - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool canFocusChildren() const override; - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override; - /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override; - - // From LLFocusableElement - /*virtual*/ void setFocus( bool b ) override; - /*virtual*/ bool hasFocus() const override; - - // New virtuals - - - // Return NULL by default (overrride if the class has the appropriate interface) - virtual class LLCtrlSelectionInterface* getSelectionInterface(); - virtual class LLCtrlListInterface* getListInterface(); - virtual class LLCtrlScrollInterface* getScrollInterface(); - - bool setControlValue(const LLSD& value); - void setControlVariable(LLControlVariable* control); - virtual void setControlName(const std::string& control, LLView *context = NULL); - void removeControlVariable(); - - LLControlVariable* getControlVariable() { return mControlVariable; } - - void setEnabledControlVariable(LLControlVariable* control); - void setDisabledControlVariable(LLControlVariable* control); - void setMakeVisibleControlVariable(LLControlVariable* control); - void setMakeInvisibleControlVariable(LLControlVariable* control); - - void setFunctionName(const std::string& function_name); - - 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); - - virtual bool acceptsTextInput() const; // Defaults to false - - // A control is dirty if the user has modified its value. - // Editable controls should override this. - virtual bool isDirty() const; // Defauls to false - virtual void resetDirty(); //Defaults to no-op - - // Call appropriate callback - virtual void onCommit(); - - // Default to no-op: - virtual void onTabInto(); - - // Clear any user-provided input (text in a text editor, checked checkbox, - // selected radio button, etc.). Defaults to no-op. - virtual void clear(); - - virtual void setColor(const LLColor4& color); - - // Ansariel: Changed to virtual. We might want to change the transparency ourself! - virtual F32 getCurrentTransparency(); - - void setTransparencyType(ETypeTransparency type); - ETypeTransparency getTransparencyType() const {return mTransparencyType;} - - bool focusNextItem(bool text_entry_only); - bool focusPrevItem(bool text_entry_only); - bool focusFirstItem(bool prefer_text_fields = false, bool focus_flash = true ); - - // Non Virtuals - LLHandle getHandle() const { return getDerivedHandle(); } - bool getIsChrome() const; - - void setTabStop( bool b ); - bool hasTabStop() const; - - LLUICtrl* getParentUICtrl() const; - - // return true if help topic found by crawling through parents - - // topic then put in help_topic_out - bool findHelpTopic(std::string& help_topic_out); - - boost::signals2::connection setCommitCallback(const CommitCallbackParam& cb); - boost::signals2::connection setValidateCallback(const EnableCallbackParam& cb); - - boost::signals2::connection setCommitCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setValidateCallback( const enable_signal_t::slot_type& cb ); - - boost::signals2::connection setMouseEnterCallback( const commit_signal_t::slot_type& cb ); - boost::signals2::connection setMouseLeaveCallback( const commit_signal_t::slot_type& cb ); - - boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); - boost::signals2::connection setMouseUpCallback( const mouse_signal_t::slot_type& cb ); - boost::signals2::connection setRightMouseDownCallback( const mouse_signal_t::slot_type& cb ); - boost::signals2::connection setRightMouseUpCallback( const mouse_signal_t::slot_type& cb ); - - boost::signals2::connection setDoubleClickCallback( const mouse_signal_t::slot_type& cb ); - - // *TODO: Deprecate; for backwards compatability only: - boost::signals2::connection setCommitCallback( boost::function cb, void* data); - boost::signals2::connection setValidateBeforeCommit( boost::function cb ); - - LLUICtrl* findRootMostFocusRoot(); - - class LLTextInputFilter : public LLQueryFilter, public LLSingleton - { - LLSINGLETON_EMPTY_CTOR(LLTextInputFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override - { - return filterResult_t(view->isCtrl() && static_cast(view)->acceptsTextInput(), true); - } - }; - - template class CallbackRegistry : public LLRegistrySingleton - {}; - - class CommitCallbackRegistry : public CallbackRegistry - { - LLSINGLETON_EMPTY_CTOR(CommitCallbackRegistry); - }; - // the enable callback registry is also used for visiblity callbacks - class EnableCallbackRegistry : public CallbackRegistry - { - LLSINGLETON_EMPTY_CTOR(EnableCallbackRegistry); - }; - -protected: - - static bool controlListener(const LLSD& newvalue, LLHandle handle, std::string type); - - commit_signal_t* mCommitSignal; - enable_signal_t* mValidateSignal; - - commit_signal_t* mMouseEnterSignal; - commit_signal_t* mMouseLeaveSignal; - - mouse_signal_t* mMouseDownSignal; - mouse_signal_t* mMouseUpSignal; - mouse_signal_t* mRightMouseDownSignal; - mouse_signal_t* mRightMouseUpSignal; - - mouse_signal_t* mDoubleClickSignal; - - LLViewModelPtr mViewModel; - - LLControlVariable* mControlVariable; - boost::signals2::connection mControlConnection; - LLControlVariable* mEnabledControlVariable; - boost::signals2::connection mEnabledControlConnection; - LLControlVariable* mDisabledControlVariable; - boost::signals2::connection mDisabledControlConnection; - LLControlVariable* mMakeVisibleControlVariable; - boost::signals2::connection mMakeVisibleControlConnection; - LLControlVariable* mMakeInvisibleControlVariable; - boost::signals2::connection mMakeInvisibleControlConnection; - - std::string mFunctionName; - - static F32 sActiveControlTransparency; - static F32 sInactiveControlTransparency; - - /*virtual*/ void addInfo(LLSD & info) override; - -private: - - bool mIsChrome; - bool mRequestsFront; - bool mTabStop; - bool mTentative; - - ETypeTransparency mTransparencyType; -}; - -// Build time optimization, generate once in .cpp file -#ifndef LLUICTRL_CPP -extern template class LLUICtrl* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -#endif // LL_LLUICTRL_H +/** + * @file lluictrl.h + * @author James Cook, Richard Nelson, Tom Yedwab + * @brief Abstract base class for UI controls + * + * $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$ + */ + +#ifndef LL_LLUICTRL_H +#define LL_LLUICTRL_H + +//#include "llboost.h" +#include "llrect.h" +#include "llsd.h" +#include +#include + +#include "llinitparam.h" +#include "llview.h" +#include "llviewmodel.h" // *TODO move dependency to .cpp file +#include "llsearchablecontrol.h" + +const bool TAKE_FOCUS_YES = true; +const bool TAKE_FOCUS_NO = false; +const S32 DROP_SHADOW_FLOATER = 5; + +class LLUICtrl + : public LLView, public boost::signals2::trackable +{ +public: + typedef boost::function commit_callback_t; + typedef boost::signals2::signal commit_signal_t; + // *TODO: add xml support for this type of signal in the future + typedef boost::signals2::signal mouse_signal_t; + + typedef boost::function enable_callback_t; + typedef boost::signals2::signal enable_signal_t; + + struct CallbackParam : public LLInitParam::Block + { + Ignored name; + + Optional function_name; + Optional parameter; + + Optional control_name; + + CallbackParam(); + }; + + struct CommitCallbackParam : public LLInitParam::Block + { + Optional function; + }; + + // also used for visible callbacks + struct EnableCallbackParam : public LLInitParam::Block + { + Optional function; + }; + + struct EnableControls : public LLInitParam::ChoiceBlock + { + Alternative enabled; + Alternative disabled; + + EnableControls(); + }; + struct ControlVisibility : public LLInitParam::ChoiceBlock + { + Alternative visible; + Alternative invisible; + + ControlVisibility(); + }; + struct Params : public LLInitParam::Block + { + Optional label; + Optional tab_stop, + chrome, + requests_front; + Optional initial_value; + + Optional init_callback, + commit_callback; + Optional validate_callback; + + Optional mouseenter_callback, + mouseleave_callback; + + Optional control_name; + Optional enabled_controls; + Optional controls_visibility; + + // font params + Optional font; + Optional font_halign; + Optional font_valign; + + // cruft from LLXMLNode implementation + Ignored type, + length; + + Params(); + }; + + enum ETypeTransparency + { + TT_DEFAULT, + TT_ACTIVE, // focused floater + TT_INACTIVE, // other floaters + TT_FADING, // fading toast + }; + /*virtual*/ ~LLUICtrl(); + + void initFromParams(const Params& p); +protected: + friend class LLUICtrlFactory; + static const Params& getDefaultParams(); + LLUICtrl(const Params& p = getDefaultParams(), + const LLViewModelPtr& viewmodel=LLViewModelPtr(new LLViewModel)); + + commit_signal_t::slot_type initCommitCallback(const CommitCallbackParam& cb); + enable_signal_t::slot_type initEnableCallback(const EnableCallbackParam& cb); + + // 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&); + + /*virtual*/ bool postBuild() override; + +public: + // LLView interface + /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text ) override; + /*virtual*/ bool isCtrl() const override; + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override; + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool canFocusChildren() const override; + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override; + + // From LLFocusableElement + /*virtual*/ void setFocus( bool b ) override; + /*virtual*/ bool hasFocus() const override; + + // New virtuals + + + // Return NULL by default (overrride if the class has the appropriate interface) + virtual class LLCtrlSelectionInterface* getSelectionInterface(); + virtual class LLCtrlListInterface* getListInterface(); + virtual class LLCtrlScrollInterface* getScrollInterface(); + + bool setControlValue(const LLSD& value); + void setControlVariable(LLControlVariable* control); + virtual void setControlName(const std::string& control, LLView *context = NULL); + void removeControlVariable(); + + LLControlVariable* getControlVariable() { return mControlVariable; } + + void setEnabledControlVariable(LLControlVariable* control); + void setDisabledControlVariable(LLControlVariable* control); + void setMakeVisibleControlVariable(LLControlVariable* control); + void setMakeInvisibleControlVariable(LLControlVariable* control); + + void setFunctionName(const std::string& function_name); + + 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); + + virtual bool acceptsTextInput() const; // Defaults to false + + // A control is dirty if the user has modified its value. + // Editable controls should override this. + virtual bool isDirty() const; // Defauls to false + virtual void resetDirty(); //Defaults to no-op + + // Call appropriate callback + virtual void onCommit(); + + // Default to no-op: + virtual void onTabInto(); + + // Clear any user-provided input (text in a text editor, checked checkbox, + // selected radio button, etc.). Defaults to no-op. + virtual void clear(); + + virtual void setColor(const LLColor4& color); + + // Ansariel: Changed to virtual. We might want to change the transparency ourself! + virtual F32 getCurrentTransparency(); + + void setTransparencyType(ETypeTransparency type); + ETypeTransparency getTransparencyType() const {return mTransparencyType;} + + bool focusNextItem(bool text_entry_only); + bool focusPrevItem(bool text_entry_only); + bool focusFirstItem(bool prefer_text_fields = false, bool focus_flash = true ); + + // Non Virtuals + LLHandle getHandle() const { return getDerivedHandle(); } + bool getIsChrome() const; + + void setTabStop( bool b ); + bool hasTabStop() const; + + LLUICtrl* getParentUICtrl() const; + + // return true if help topic found by crawling through parents - + // topic then put in help_topic_out + bool findHelpTopic(std::string& help_topic_out); + + boost::signals2::connection setCommitCallback(const CommitCallbackParam& cb); + boost::signals2::connection setValidateCallback(const EnableCallbackParam& cb); + + boost::signals2::connection setCommitCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setValidateCallback( const enable_signal_t::slot_type& cb ); + + boost::signals2::connection setMouseEnterCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setMouseLeaveCallback( const commit_signal_t::slot_type& cb ); + + boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); + boost::signals2::connection setMouseUpCallback( const mouse_signal_t::slot_type& cb ); + boost::signals2::connection setRightMouseDownCallback( const mouse_signal_t::slot_type& cb ); + boost::signals2::connection setRightMouseUpCallback( const mouse_signal_t::slot_type& cb ); + + boost::signals2::connection setDoubleClickCallback( const mouse_signal_t::slot_type& cb ); + + // *TODO: Deprecate; for backwards compatability only: + boost::signals2::connection setCommitCallback( boost::function cb, void* data); + boost::signals2::connection setValidateBeforeCommit( boost::function cb ); + + LLUICtrl* findRootMostFocusRoot(); + + class LLTextInputFilter : public LLQueryFilter, public LLSingleton + { + LLSINGLETON_EMPTY_CTOR(LLTextInputFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override + { + return filterResult_t(view->isCtrl() && static_cast(view)->acceptsTextInput(), true); + } + }; + + template class CallbackRegistry : public LLRegistrySingleton + {}; + + class CommitCallbackRegistry : public CallbackRegistry + { + LLSINGLETON_EMPTY_CTOR(CommitCallbackRegistry); + }; + // the enable callback registry is also used for visiblity callbacks + class EnableCallbackRegistry : public CallbackRegistry + { + LLSINGLETON_EMPTY_CTOR(EnableCallbackRegistry); + }; + +protected: + + static bool controlListener(const LLSD& newvalue, LLHandle handle, std::string type); + + commit_signal_t* mCommitSignal; + enable_signal_t* mValidateSignal; + + commit_signal_t* mMouseEnterSignal; + commit_signal_t* mMouseLeaveSignal; + + mouse_signal_t* mMouseDownSignal; + mouse_signal_t* mMouseUpSignal; + mouse_signal_t* mRightMouseDownSignal; + mouse_signal_t* mRightMouseUpSignal; + + mouse_signal_t* mDoubleClickSignal; + + LLViewModelPtr mViewModel; + + LLControlVariable* mControlVariable; + boost::signals2::connection mControlConnection; + LLControlVariable* mEnabledControlVariable; + boost::signals2::connection mEnabledControlConnection; + LLControlVariable* mDisabledControlVariable; + boost::signals2::connection mDisabledControlConnection; + LLControlVariable* mMakeVisibleControlVariable; + boost::signals2::connection mMakeVisibleControlConnection; + LLControlVariable* mMakeInvisibleControlVariable; + boost::signals2::connection mMakeInvisibleControlConnection; + + std::string mFunctionName; + + static F32 sActiveControlTransparency; + static F32 sInactiveControlTransparency; + + /*virtual*/ void addInfo(LLSD & info) override; + +private: + + bool mIsChrome; + bool mRequestsFront; + bool mTabStop; + bool mTentative; + + ETypeTransparency mTransparencyType; +}; + +// Build time optimization, generate once in .cpp file +#ifndef LLUICTRL_CPP +extern template class LLUICtrl* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +#endif // LL_LLUICTRL_H diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index e7b98629d2..a2a6d661ff 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -1,279 +1,279 @@ -/** - * @file lluictrlfactory.cpp - * @brief Factory class for creating UI controls - * - * $LicenseInfo:firstyear=2003&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" - -#define LLUICTRLFACTORY_CPP -#include "lluictrlfactory.h" - -#include "llxmlnode.h" - -#include -#include - -// other library includes -#include "llcontrol.h" -#include "lldir.h" -#include "v4color.h" -#include "v3dmath.h" -#include "llquaternion.h" - -// this library includes -#include "llpanel.h" - -//----------------------------------------------------------------------------- - -// UI Ctrl class for padding -class LLUICtrlLocate : public LLUICtrl -{ -public: - struct Params : public LLInitParam::Block - { - Params() - { - name = "locate"; - tab_stop = false; - } - }; - - LLUICtrlLocate(const Params& p) : LLUICtrl(p) {} - virtual void draw() { } - -}; - -static LLDefaultChildRegistry::Register r1("locate"); - -// Build time optimization, generate this once in .cpp file -template class LLUICtrlFactory* LLSingleton::getInstance(); - -//----------------------------------------------------------------------------- -// LLUICtrlFactory() -//----------------------------------------------------------------------------- -LLUICtrlFactory::LLUICtrlFactory() - : mDummyPanel(NULL) // instantiated when first needed -{ -} - -LLUICtrlFactory::~LLUICtrlFactory() -{ - // go ahead and leak mDummyPanel since this is static destructor time - //delete mDummyPanel; - //mDummyPanel = NULL; -} - -void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block) -{ - std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml"); - LLXMLNodePtr root_node; - std::vector search_paths = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, filename); - - if (search_paths.empty()) - { - return; - } - - // "en" version, the default-language version of the file. - std::string base_filename = search_paths.front(); - if (!base_filename.empty()) - { - LLUICtrlFactory::instance().pushFileName(base_filename); - - if (!LLXMLNode::getLayeredXMLNode(root_node, search_paths)) - { - LL_WARNS() << "Couldn't parse widget from: " << base_filename << LL_ENDL; - return; - } - LLXUIParser parser; - parser.readXUI(root_node, block, base_filename); - LLUICtrlFactory::instance().popFileName(); - } -} - -//static -void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t& registry, LLXMLNodePtr output_node) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - if (node.isNull()) return; - - for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling()) - { - LLXMLNodePtr outputChild; - if (output_node) - { - outputChild = output_node->createChild("", false); - } - - if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild)) - { - // child_node is not a valid child for the current parent - std::string child_name = std::string(child_node->getName()->mString); - if (LLDefaultChildRegistry::instance().getValue(child_name)) - { - // This means that the registry assocaited with the parent widget does not have an entry - // for the child widget - // You might need to add something like: - // static ParentWidgetRegistry::Register register("child_widget_name"); - LL_WARNS() << child_name << " is not a valid child of " << node->getName()->mString << LL_ENDL; - } - else - { - LL_WARNS() << "Could not create widget named " << child_node->getName()->mString << LL_ENDL; - } - } - - if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty()) - { - output_node->deleteChild(outputChild); - } - } - -} - -//----------------------------------------------------------------------------- -// getLayeredXMLNode() -//----------------------------------------------------------------------------- -bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root, - LLDir::ESkinConstraint constraint) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - std::vector paths = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint); - - if (paths.empty()) - { - // sometimes whole path is passed in as filename - paths.push_back(xui_filename); - } - - return LLXMLNode::getLayeredXMLNode(root, paths); -} - - -//----------------------------------------------------------------------------- -// saveToXML() -//----------------------------------------------------------------------------- -S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename) -{ - return 0; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - std::string ctrl_type = node->getName()->mString; - LLStringUtil::toLower(ctrl_type); - - const LLWidgetCreatorFunc* funcp = registry.getValue(ctrl_type); - if (funcp == NULL) - { - return NULL; - } - - if (parent == NULL) - { - if (mDummyPanel == NULL) - { - LLPanel::Params p; - mDummyPanel = create(p); - } - parent = mDummyPanel; - } - LLView *view = (*funcp)(node, parent, output_node); - - return view; -} - -std::string LLUICtrlFactory::getCurFileName() -{ - return mFileNames.empty() ? "" : mFileNames.back(); -} - - -void LLUICtrlFactory::pushFileName(const std::string& name) -{ - // Here we seem to be looking for the default language file ("en") rather - // than the localized one, if any. - mFileNames.push_back(gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, name)); -} - -void LLUICtrlFactory::popFileName() -{ - mFileNames.pop_back(); -} - -//static -void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group) -{ - if (tab_group == S32_MAX) tab_group = parent->getLastTabGroup(); - parent->addChild(view, tab_group); -} - -//static -void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest) -{ - dest->setName(src->getName()->mString); -} - -template -const LLInitParam::BaseBlock& get_empty_param_block() -{ - static typename T::Params params; - return params; -} - -// adds a widget and its param block to various registries -//static -void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& name) -{ - // associate parameter block type with template .xml file - std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type); - if (existing_name != NULL) - { - if(*existing_name != name) - { - std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl; - // forcing crash here - char* foo = 0; - *foo = 1; - } - else - { - // widget already registered this name - return; - } - } - - LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, name); - //FIXME: comment this in when working on schema generation - //LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type); - //LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &get_empty_param_block); -} - - +/** + * @file lluictrlfactory.cpp + * @brief Factory class for creating UI controls + * + * $LicenseInfo:firstyear=2003&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" + +#define LLUICTRLFACTORY_CPP +#include "lluictrlfactory.h" + +#include "llxmlnode.h" + +#include +#include + +// other library includes +#include "llcontrol.h" +#include "lldir.h" +#include "v4color.h" +#include "v3dmath.h" +#include "llquaternion.h" + +// this library includes +#include "llpanel.h" + +//----------------------------------------------------------------------------- + +// UI Ctrl class for padding +class LLUICtrlLocate : public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Params() + { + name = "locate"; + tab_stop = false; + } + }; + + LLUICtrlLocate(const Params& p) : LLUICtrl(p) {} + virtual void draw() { } + +}; + +static LLDefaultChildRegistry::Register r1("locate"); + +// Build time optimization, generate this once in .cpp file +template class LLUICtrlFactory* LLSingleton::getInstance(); + +//----------------------------------------------------------------------------- +// LLUICtrlFactory() +//----------------------------------------------------------------------------- +LLUICtrlFactory::LLUICtrlFactory() + : mDummyPanel(NULL) // instantiated when first needed +{ +} + +LLUICtrlFactory::~LLUICtrlFactory() +{ + // go ahead and leak mDummyPanel since this is static destructor time + //delete mDummyPanel; + //mDummyPanel = NULL; +} + +void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block) +{ + std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml"); + LLXMLNodePtr root_node; + std::vector search_paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, filename); + + if (search_paths.empty()) + { + return; + } + + // "en" version, the default-language version of the file. + std::string base_filename = search_paths.front(); + if (!base_filename.empty()) + { + LLUICtrlFactory::instance().pushFileName(base_filename); + + if (!LLXMLNode::getLayeredXMLNode(root_node, search_paths)) + { + LL_WARNS() << "Couldn't parse widget from: " << base_filename << LL_ENDL; + return; + } + LLXUIParser parser; + parser.readXUI(root_node, block, base_filename); + LLUICtrlFactory::instance().popFileName(); + } +} + +//static +void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t& registry, LLXMLNodePtr output_node) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + if (node.isNull()) return; + + for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling()) + { + LLXMLNodePtr outputChild; + if (output_node) + { + outputChild = output_node->createChild("", false); + } + + if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild)) + { + // child_node is not a valid child for the current parent + std::string child_name = std::string(child_node->getName()->mString); + if (LLDefaultChildRegistry::instance().getValue(child_name)) + { + // This means that the registry assocaited with the parent widget does not have an entry + // for the child widget + // You might need to add something like: + // static ParentWidgetRegistry::Register register("child_widget_name"); + LL_WARNS() << child_name << " is not a valid child of " << node->getName()->mString << LL_ENDL; + } + else + { + LL_WARNS() << "Could not create widget named " << child_node->getName()->mString << LL_ENDL; + } + } + + if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty()) + { + output_node->deleteChild(outputChild); + } + } + +} + +//----------------------------------------------------------------------------- +// getLayeredXMLNode() +//----------------------------------------------------------------------------- +bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root, + LLDir::ESkinConstraint constraint) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + std::vector paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint); + + if (paths.empty()) + { + // sometimes whole path is passed in as filename + paths.push_back(xui_filename); + } + + return LLXMLNode::getLayeredXMLNode(root, paths); +} + + +//----------------------------------------------------------------------------- +// saveToXML() +//----------------------------------------------------------------------------- +S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename) +{ + return 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + std::string ctrl_type = node->getName()->mString; + LLStringUtil::toLower(ctrl_type); + + const LLWidgetCreatorFunc* funcp = registry.getValue(ctrl_type); + if (funcp == NULL) + { + return NULL; + } + + if (parent == NULL) + { + if (mDummyPanel == NULL) + { + LLPanel::Params p; + mDummyPanel = create(p); + } + parent = mDummyPanel; + } + LLView *view = (*funcp)(node, parent, output_node); + + return view; +} + +std::string LLUICtrlFactory::getCurFileName() +{ + return mFileNames.empty() ? "" : mFileNames.back(); +} + + +void LLUICtrlFactory::pushFileName(const std::string& name) +{ + // Here we seem to be looking for the default language file ("en") rather + // than the localized one, if any. + mFileNames.push_back(gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, name)); +} + +void LLUICtrlFactory::popFileName() +{ + mFileNames.pop_back(); +} + +//static +void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group) +{ + if (tab_group == S32_MAX) tab_group = parent->getLastTabGroup(); + parent->addChild(view, tab_group); +} + +//static +void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest) +{ + dest->setName(src->getName()->mString); +} + +template +const LLInitParam::BaseBlock& get_empty_param_block() +{ + static typename T::Params params; + return params; +} + +// adds a widget and its param block to various registries +//static +void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& name) +{ + // associate parameter block type with template .xml file + std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type); + if (existing_name != NULL) + { + if(*existing_name != name) + { + std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl; + // forcing crash here + char* foo = 0; + *foo = 1; + } + else + { + // widget already registered this name + return; + } + } + + LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, name); + //FIXME: comment this in when working on schema generation + //LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type); + //LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &get_empty_param_block); +} + + diff --git a/indra/llui/llundo.cpp b/indra/llui/llundo.cpp index 94b250d51e..0928cb4417 100644 --- a/indra/llui/llundo.cpp +++ b/indra/llui/llundo.cpp @@ -1,174 +1,174 @@ -/** - * @file llundo.cpp - * - * $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 "linden_common.h" -#include "llundo.h" - - -// TODO: -// implement doubly linked circular list for ring buffer -// this will allow us to easily change the size of an undo buffer on the fly - -//----------------------------------------------------------------------------- -// LLUndoBuffer() -//----------------------------------------------------------------------------- -LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count ) -{ - mNextAction = 0; - mLastAction = 0; - mFirstAction = 0; - mOperationID = 0; - - mNumActions = initial_count; - - mActions = new LLUndoAction *[initial_count]; - - //initialize buffer with actions - for (S32 i = 0; i < initial_count; i++) - { - mActions[i] = create_func(); - if (!mActions[i]) - { - LL_ERRS() << "Unable to create action for undo buffer" << LL_ENDL; - } - } -} - -//----------------------------------------------------------------------------- -// ~LLUndoBuffer() -//----------------------------------------------------------------------------- -LLUndoBuffer::~LLUndoBuffer() -{ - for (S32 i = 0; i < mNumActions; i++) - { - delete mActions[i]; - } - - delete [] mActions; -} - -//----------------------------------------------------------------------------- -// getNextAction() -//----------------------------------------------------------------------------- -LLUndoBuffer::LLUndoAction* LLUndoBuffer::getNextAction(bool setClusterBegin) -{ - LLUndoAction *nextAction = mActions[mNextAction]; - - if (setClusterBegin) - { - mOperationID++; - } - mActions[mNextAction]->mClusterID = mOperationID; - - mNextAction = (mNextAction + 1) % mNumActions; - mLastAction = mNextAction; - - if (mNextAction == mFirstAction) - { - mActions[mFirstAction]->cleanup(); - mFirstAction = (mFirstAction + 1) % mNumActions; - } - - return nextAction; -} - -//----------------------------------------------------------------------------- -// undoAction() -//----------------------------------------------------------------------------- -bool LLUndoBuffer::undoAction() -{ - if (!canUndo()) - { - return false; - } - - S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions; - - while(mActions[prevAction]->mClusterID == mOperationID) - { - // go ahead and decrement action index - mNextAction = prevAction; - - // undo this action - mActions[mNextAction]->undo(); - - // we're at the first action, so we don't know if we've actually undid everything - if (mNextAction == mFirstAction) - { - mOperationID--; - return false; - } - - // do wrap-around of index, but avoid negative numbers for modulo operator - prevAction = (mNextAction + mNumActions - 1) % mNumActions; - } - - mOperationID--; - - return true; -} - -//----------------------------------------------------------------------------- -// redoAction() -//----------------------------------------------------------------------------- -bool LLUndoBuffer::redoAction() -{ - if (!canRedo()) - { - return false; - } - - mOperationID++; - - while(mActions[mNextAction]->mClusterID == mOperationID) - { - if (mNextAction == mLastAction) - { - return false; - } - - mActions[mNextAction]->redo(); - - // do wrap-around of index - mNextAction = (mNextAction + 1) % mNumActions; - } - - return true; -} - -//----------------------------------------------------------------------------- -// flushActions() -//----------------------------------------------------------------------------- -void LLUndoBuffer::flushActions() -{ - for (S32 i = 0; i < mNumActions; i++) - { - mActions[i]->cleanup(); - } - mNextAction = 0; - mLastAction = 0; - mFirstAction = 0; - mOperationID = 0; -} +/** + * @file llundo.cpp + * + * $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 "linden_common.h" +#include "llundo.h" + + +// TODO: +// implement doubly linked circular list for ring buffer +// this will allow us to easily change the size of an undo buffer on the fly + +//----------------------------------------------------------------------------- +// LLUndoBuffer() +//----------------------------------------------------------------------------- +LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count ) +{ + mNextAction = 0; + mLastAction = 0; + mFirstAction = 0; + mOperationID = 0; + + mNumActions = initial_count; + + mActions = new LLUndoAction *[initial_count]; + + //initialize buffer with actions + for (S32 i = 0; i < initial_count; i++) + { + mActions[i] = create_func(); + if (!mActions[i]) + { + LL_ERRS() << "Unable to create action for undo buffer" << LL_ENDL; + } + } +} + +//----------------------------------------------------------------------------- +// ~LLUndoBuffer() +//----------------------------------------------------------------------------- +LLUndoBuffer::~LLUndoBuffer() +{ + for (S32 i = 0; i < mNumActions; i++) + { + delete mActions[i]; + } + + delete [] mActions; +} + +//----------------------------------------------------------------------------- +// getNextAction() +//----------------------------------------------------------------------------- +LLUndoBuffer::LLUndoAction* LLUndoBuffer::getNextAction(bool setClusterBegin) +{ + LLUndoAction *nextAction = mActions[mNextAction]; + + if (setClusterBegin) + { + mOperationID++; + } + mActions[mNextAction]->mClusterID = mOperationID; + + mNextAction = (mNextAction + 1) % mNumActions; + mLastAction = mNextAction; + + if (mNextAction == mFirstAction) + { + mActions[mFirstAction]->cleanup(); + mFirstAction = (mFirstAction + 1) % mNumActions; + } + + return nextAction; +} + +//----------------------------------------------------------------------------- +// undoAction() +//----------------------------------------------------------------------------- +bool LLUndoBuffer::undoAction() +{ + if (!canUndo()) + { + return false; + } + + S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions; + + while(mActions[prevAction]->mClusterID == mOperationID) + { + // go ahead and decrement action index + mNextAction = prevAction; + + // undo this action + mActions[mNextAction]->undo(); + + // we're at the first action, so we don't know if we've actually undid everything + if (mNextAction == mFirstAction) + { + mOperationID--; + return false; + } + + // do wrap-around of index, but avoid negative numbers for modulo operator + prevAction = (mNextAction + mNumActions - 1) % mNumActions; + } + + mOperationID--; + + return true; +} + +//----------------------------------------------------------------------------- +// redoAction() +//----------------------------------------------------------------------------- +bool LLUndoBuffer::redoAction() +{ + if (!canRedo()) + { + return false; + } + + mOperationID++; + + while(mActions[mNextAction]->mClusterID == mOperationID) + { + if (mNextAction == mLastAction) + { + return false; + } + + mActions[mNextAction]->redo(); + + // do wrap-around of index + mNextAction = (mNextAction + 1) % mNumActions; + } + + return true; +} + +//----------------------------------------------------------------------------- +// flushActions() +//----------------------------------------------------------------------------- +void LLUndoBuffer::flushActions() +{ + for (S32 i = 0; i < mNumActions; i++) + { + mActions[i]->cleanup(); + } + mNextAction = 0; + mLastAction = 0; + mFirstAction = 0; + mOperationID = 0; +} diff --git a/indra/llui/llundo.h b/indra/llui/llundo.h index 41e8a2b2f4..dc40702be0 100644 --- a/indra/llui/llundo.h +++ b/indra/llui/llundo.h @@ -1,68 +1,68 @@ -/** - * @file llundo.h - * @brief Generic interface for undo/redo circular buffer. - * - * $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_LLUNDO_H -#define LL_LLUNDO_H - - -class LLUndoBuffer -{ -public: - class LLUndoAction - { - friend class LLUndoBuffer; - public: - virtual void undo() = 0; - virtual void redo() = 0; - virtual void cleanup() {}; - protected: - LLUndoAction(): mClusterID(0) {}; - virtual ~LLUndoAction(){}; - private: - S32 mClusterID; - }; - - LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count ); - virtual ~LLUndoBuffer(); - - LLUndoAction *getNextAction(bool setClusterBegin = true); - bool undoAction(); - bool redoAction(); - bool canUndo() { return (mNextAction != mFirstAction); } - bool canRedo() { return (mNextAction != mLastAction); } - - void flushActions(); - -private: - LLUndoAction **mActions; // array of pointers to undoactions - S32 mNumActions; // total number of actions in ring buffer - S32 mNextAction; // next action to perform undo/redo on - S32 mLastAction; // last action actually added to undo buffer - S32 mFirstAction; // beginning of ring buffer (don't undo any further) - S32 mOperationID; // current operation id, for undoing and redoing in clusters -}; - -#endif //LL_LLUNDO_H +/** + * @file llundo.h + * @brief Generic interface for undo/redo circular buffer. + * + * $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_LLUNDO_H +#define LL_LLUNDO_H + + +class LLUndoBuffer +{ +public: + class LLUndoAction + { + friend class LLUndoBuffer; + public: + virtual void undo() = 0; + virtual void redo() = 0; + virtual void cleanup() {}; + protected: + LLUndoAction(): mClusterID(0) {}; + virtual ~LLUndoAction(){}; + private: + S32 mClusterID; + }; + + LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count ); + virtual ~LLUndoBuffer(); + + LLUndoAction *getNextAction(bool setClusterBegin = true); + bool undoAction(); + bool redoAction(); + bool canUndo() { return (mNextAction != mFirstAction); } + bool canRedo() { return (mNextAction != mLastAction); } + + void flushActions(); + +private: + LLUndoAction **mActions; // array of pointers to undoactions + S32 mNumActions; // total number of actions in ring buffer + S32 mNextAction; // next action to perform undo/redo on + S32 mLastAction; // last action actually added to undo buffer + S32 mFirstAction; // beginning of ring buffer (don't undo any further) + S32 mOperationID; // current operation id, for undoing and redoing in clusters +}; + +#endif //LL_LLUNDO_H diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 485dfd0abb..234ce9f4c3 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -1,1738 +1,1738 @@ -/** - * @file llurlentry.cpp - * @author Martin Reddy - * @brief Describes the Url types that can be registered in LLUrlRegistry - * - * $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 "llurlentry.h" -#include "lluictrl.h" -#include "lluri.h" -#include "llurlmatch.h" -#include "llurlregistry.h" -#include "lluriparser.h" - -#include "llavatarnamecache.h" -#include "llcachename.h" -#include "llkeyboard.h" -#include "llregex.h" -#include "llscrolllistctrl.h" // for LLUrlEntryKeybinding file parsing -#include "lltrans.h" -#include "lluicolortable.h" -#include "message.h" -#include "llexperiencecache.h" - -#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" - -// Utility functions -std::string localize_slapp_label(const std::string& url, const std::string& full_name); - - -LLUrlEntryBase::LLUrlEntryBase() -{ -} - -LLUrlEntryBase::~LLUrlEntryBase() -{ -} - -std::string LLUrlEntryBase::getUrl(const std::string &string) const -{ - return escapeUrl(string); -} - -//virtual -std::string LLUrlEntryBase::getIcon(const std::string &url) -{ - return mIcon; -} - -LLStyle::Params LLUrlEntryBase::getStyle() const -{ - LLStyle::Params style_params; - style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.font.style = "UNDERLINE"; - return style_params; -} - - -std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const -{ - // return the id from a SLURL in the format /app/{cmd}/{id}/about - LLURI uri(url); - LLSD path_array = uri.pathArray(); - if (path_array.size() == 4) - { - return path_array.get(2).asString(); - } - return ""; -} - -std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const -{ - return LLURI::unescape(url); -} - -std::string LLUrlEntryBase::escapeUrl(const std::string &url) const -{ - static std::string no_escape_chars; - static bool initialized = false; - if (!initialized) - { - no_escape_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~!$?&()*+,@:;=/%#"; - - std::sort(no_escape_chars.begin(), no_escape_chars.end()); - initialized = true; - } - return LLURI::escape(url, no_escape_chars, true); -} - -std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const -{ - // return the label part from [http://www.example.org Label] - const char *text = url.c_str(); - S32 start = 0; - while (! isspace(text[start])) - { - start++; - } - while (text[start] == ' ' || text[start] == '\t') - { - start++; - } - return unescapeUrl(url.substr(start, url.size()-start-1)); -} - -std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const -{ - // return the url part from [http://www.example.org Label] - const char *text = string.c_str(); - S32 end = 0; - while (! isspace(text[end])) - { - end++; - } - return escapeUrl(string.substr(1, end-1)); -} - -void LLUrlEntryBase::addObserver(const std::string &id, - const std::string &url, - const LLUrlLabelCallback &cb) -{ - // add a callback to be notified when we have a label for the uuid - LLUrlEntryObserver observer; - observer.url = url; - observer.signal = new LLUrlLabelSignal(); - if (observer.signal) - { - observer.signal->connect(cb); - mObservers.insert(std::pair(id, observer)); - } -} - -// *NOTE: See also LLUrlEntryAgent::callObservers() -void LLUrlEntryBase::callObservers(const std::string &id, - const std::string &label, - const std::string &icon) -{ - // notify all callbacks waiting on the given uuid - typedef std::multimap::iterator observer_it; - std::pair matching_range = mObservers.equal_range(id); - for (observer_it it = matching_range.first; it != matching_range.second;) - { - // call the callback - give it the new label - LLUrlEntryObserver &observer = it->second; - (*observer.signal)(it->second.url, label, icon); - // then remove the signal - we only need to call it once - delete observer.signal; - mObservers.erase(it++); - } -} - -/// is this a match for a URL that should not be hyperlinked? -bool LLUrlEntryBase::isLinkDisabled() const -{ - // this allows us to have a global setting to turn off text hyperlink highlighting/action - static LLCachedControl globally_disabled(*LLUI::getInstance()->mSettingGroups["config"], "DisableTextHyperlinkActions", false); - - return globally_disabled; -} - -bool LLUrlEntryBase::isWikiLinkCorrect(const std::string &labeled_url) const -{ - LLWString wlabel = utf8str_to_wstring(getLabelFromWikiLink(labeled_url)); - wlabel.erase(std::remove(wlabel.begin(), wlabel.end(), L'\u200B'), wlabel.end()); - - // Unicode URL validation, see SL-15243 - std::replace_if(wlabel.begin(), - wlabel.end(), - [](const llwchar &chr) - { - return (chr == L'\u2024') // "One Dot Leader" - || (chr == L'\uFE52') // "Small Full Stop" - || (chr == L'\uFF0E') // "Fullwidth Full Stop" - // Not a decomposition, but suficiently similar - || (chr == L'\u05C5'); // "Hebrew Mark Lower Dot" - }, - L'\u002E'); // Dot "Full Stop" - - std::replace_if(wlabel.begin(), - wlabel.end(), - [](const llwchar &chr) - { - return (chr == L'\u02D0') // "Modifier Letter Colon" - || (chr == L'\uFF1A') // "Fullwidth Colon" - || (chr == L'\u2236') // "Ratio" - || (chr == L'\uFE55'); // "Small Colon" - }, - L'\u003A'); // Colon - - std::replace_if(wlabel.begin(), - wlabel.end(), - [](const llwchar &chr) - { - return (chr == L'\uFF0F'); // "Fullwidth Solidus" - }, - L'\u002F'); // Solidus - - std::string label = wstring_to_utf8str(wlabel); - if ((label.find(".com") != std::string::npos - || label.find("www.") != std::string::npos) - && label.find("://") == std::string::npos) - { - label = "http://" + label; - } - - return !LLUrlRegistry::instance().hasUrl(label); -} - -std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const -{ - if (url.empty()) - { - return url; - } - LLUriParser up(escapeUrl(url)); - if (up.normalize() == 0) - { - std::string label; - up.extractParts(); - up.glueFirst(label); - - return unescapeUrl(label); - } - return std::string(); -} - -std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const -{ - std::string escaped_url = escapeUrl(url); - LLUriParser up(escaped_url); - - std::string label; - up.extractParts(); - up.glueFirst(label, false); - - size_t pos = escaped_url.find(label); - if (pos == std::string::npos) - { - return ""; - } - pos += label.size(); - return unescapeUrl(escaped_url.substr(pos)); -} - - -static std::string getStringAfterToken(const std::string str, const std::string token) -{ - size_t pos = str.find(token); - if (pos == std::string::npos) - { - return ""; - } - - pos += token.size(); - return str.substr(pos, str.size() - pos); -} - -// -// LLUrlEntryHTTP Describes generic http: and https: Urls -// -LLUrlEntryHTTP::LLUrlEntryHTTP() - : LLUrlEntryBase() -{ - mPattern = boost::regex("https?://([^\\s/?\\.#]+\\.?)+\\.\\w+(:\\d+)?(/\\S*)?", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_http.xml"; - mTooltip = LLTrans::getString("TooltipHttpUrl"); -} - -std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - return urlToLabelWithGreyQuery(url); -} - -std::string LLUrlEntryHTTP::getQuery(const std::string &url) const -{ - return urlToGreyQuery(url); -} - -std::string LLUrlEntryHTTP::getUrl(const std::string &string) const -{ - if (string.find("://") == std::string::npos) - { - return "http://" + escapeUrl(string); - } - return escapeUrl(string); -} - -std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const -{ - return unescapeUrl(url); -} - -// -// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label -// We use the wikipedia syntax of [http://www.example.org Text] -// -LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel() -{ - mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_http.xml"; - mTooltip = LLTrans::getString("TooltipHttpUrl"); -} - -std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - std::string label = getLabelFromWikiLink(url); - return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url); -} - -std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const -{ - return getUrl(string); -} - -std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const -{ - return getUrlFromWikiLink(string); -} - -LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL() - : LLUrlEntryBase() -{ - mPattern = boost::regex("(https?://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_http.xml"; - mTooltip = LLTrans::getString("TooltipHttpUrl"); -} - -std::string LLUrlEntryInvalidSLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - - return escapeUrl(url); -} - -std::string LLUrlEntryInvalidSLURL::getUrl(const std::string &string) const -{ - return escapeUrl(string); -} - -std::string LLUrlEntryInvalidSLURL::getTooltip(const std::string &url) const -{ - return unescapeUrl(url); -} - -bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const -{ - S32 actual_parts; - - if(url.find(".com/secondlife/") != std::string::npos) - { - actual_parts = 5; - } - else if(url.find("/app/") != std::string::npos) - { - actual_parts = 6; - } - else - { - actual_parts = 3; - } - - LLURI uri(url); - LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); - S32 x,y,z; - - if (path_parts == actual_parts) - { - // handle slurl with (X,Y,Z) coordinates - LLStringUtil::convertToS32(path_array[path_parts-3],x); - LLStringUtil::convertToS32(path_array[path_parts-2],y); - LLStringUtil::convertToS32(path_array[path_parts-1],z); - - if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0)) - { - return true; - } - } - else if (path_parts == (actual_parts-1)) - { - // handle slurl with (X,Y) coordinates - - LLStringUtil::convertToS32(path_array[path_parts-2],x); - LLStringUtil::convertToS32(path_array[path_parts-1],y); - ; - if((x>= 0 && x<= 256) && (y>= 0 && y<= 256)) - { - return true; - } - } - else if (path_parts == (actual_parts-2)) - { - // handle slurl with (X) coordinate - LLStringUtil::convertToS32(path_array[path_parts-1],x); - if(x>= 0 && x<= 256) - { - return true; - } - } - - return false; -} - -// -// LLUrlEntrySLURL Describes generic http: and https: Urls -// -LLUrlEntrySLURL::LLUrlEntrySLURL() -{ - // see http://slurl.com/about.php for details on the SLURL format - mPattern = boost::regex("https?://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", - boost::regex::perl|boost::regex::icase); - mIcon = "Hand"; - mMenuName = "menu_url_slurl.xml"; - mTooltip = LLTrans::getString("TooltipSLURL"); -} - -std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - // - // we handle SLURLs in the following formats: - // - http://slurl.com/secondlife/Place/X/Y/Z - // - http://slurl.com/secondlife/Place/X/Y - // - http://slurl.com/secondlife/Place/X - // - http://slurl.com/secondlife/Place - // - - LLURI uri(url); - LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); - if (path_parts == 5) - { - // handle slurl with (X,Y,Z) coordinates - std::string location = unescapeUrl(path_array[path_parts-4]); - std::string x = path_array[path_parts-3]; - std::string y = path_array[path_parts-2]; - std::string z = path_array[path_parts-1]; - return location + " (" + x + "," + y + "," + z + ")"; - } - else if (path_parts == 4) - { - // handle slurl with (X,Y) coordinates - std::string location = unescapeUrl(path_array[path_parts-3]); - std::string x = path_array[path_parts-2]; - std::string y = path_array[path_parts-1]; - return location + " (" + x + "," + y + ")"; - } - else if (path_parts == 3) - { - // handle slurl with (X) coordinate - std::string location = unescapeUrl(path_array[path_parts-2]); - std::string x = path_array[path_parts-1]; - return location + " (" + x + ")"; - } - else if (path_parts == 2) - { - // handle slurl with no coordinates - std::string location = unescapeUrl(path_array[path_parts-1]); - return location; - } - - return url; -} - -std::string LLUrlEntrySLURL::getLocation(const std::string &url) const -{ - // return the part of the Url after slurl.com/secondlife/ - const std::string search_string = "/secondlife"; - size_t pos = url.find(search_string); - if (pos == std::string::npos) - { - return ""; - } - - pos += search_string.size() + 1; - return url.substr(pos, url.size() - pos); -} - -// -// LLUrlEntrySeconlifeURL Describes *secondlife.com/ *lindenlab.com/ *secondlifegrid.net/ and *tilia-inc.com/ urls to substitute icon 'hand.png' before link -// -LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL() -{ - mPattern = boost::regex("((http://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com)" - "|" - "(http://([-\\w\\.]*\\.)?secondlifegrid\\.net)" - "|" - "(https://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(:\\d{1,5})?)" - "|" - "(https://([-\\w\\.]*\\.)?secondlifegrid\\.net(:\\d{1,5})?)" - "|" - "(https?://([-\\w\\.]*\\.)?secondlife\\.io(:\\d{1,5})?))" - "\\/\\S*", - boost::regex::perl|boost::regex::icase); - - mIcon = "Hand"; - mMenuName = "menu_url_http.xml"; - mTooltip = LLTrans::getString("TooltipHttpUrl"); -} - -/// Return the url from a string that matched the regex -std::string LLUrlEntrySecondlifeURL::getUrl(const std::string &string) const -{ - if (string.find("://") == std::string::npos) - { - return "https://" + escapeUrl(string); - } - return escapeUrl(string); -} - -std::string LLUrlEntrySecondlifeURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - return urlToLabelWithGreyQuery(url); -} - -std::string LLUrlEntrySecondlifeURL::getQuery(const std::string &url) const -{ - return urlToGreyQuery(url); -} - -std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const -{ - return url; -} - -// -// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com *lindenlab.com *secondlifegrid.net and *tilia-inc.com urls to substitute icon 'hand.png' before link -// -LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() - { - mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(?!\\S)" - "|" - "https?://([-\\w\\.]*\\.)?secondlifegrid\\.net(?!\\S)", - boost::regex::perl|boost::regex::icase); - - mIcon = "Hand"; - mMenuName = "menu_url_http.xml"; -} - -// -// LLUrlEntryAgent Describes a Second Life agent Url, e.g., -// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about -// -LLUrlEntryAgent::LLUrlEntryAgent() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_agent.xml"; - mIcon = "Generic_Person"; -} - -// virtual -void LLUrlEntryAgent::callObservers(const std::string &id, - const std::string &label, - const std::string &icon) -{ - // notify all callbacks waiting on the given uuid - typedef std::multimap::iterator observer_it; - std::pair matching_range = mObservers.equal_range(id); - for (observer_it it = matching_range.first; it != matching_range.second;) - { - // call the callback - give it the new label - LLUrlEntryObserver &observer = it->second; - std::string final_label = localize_slapp_label(observer.url, label); - (*observer.signal)(observer.url, final_label, icon); - // then remove the signal - we only need to call it once - delete observer.signal; - mObservers.erase(it++); - } -} - -void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, - const LLAvatarName& av_name) -{ - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); - if (it != mAvatarNameCacheConnections.end()) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - mAvatarNameCacheConnections.erase(it); - } - - std::string label = av_name.getCompleteName(); - - // received the agent name from the server - tell our observers - callObservers(id.asString(), label, mIcon); -} - -LLUUID LLUrlEntryAgent::getID(const std::string &string) const -{ - return LLUUID(getIDStringFromUrl(string)); -} - -std::string LLUrlEntryAgent::getTooltip(const std::string &string) const -{ - // return a tooltip corresponding to the URL type instead of the generic one - std::string url = getUrl(string); - - if (LLStringUtil::endsWith(url, "/inspect")) - { - return LLTrans::getString("TooltipAgentInspect"); - } - if (LLStringUtil::endsWith(url, "/mute")) - { - return LLTrans::getString("TooltipAgentMute"); - } - if (LLStringUtil::endsWith(url, "/unmute")) - { - return LLTrans::getString("TooltipAgentUnmute"); - } - if (LLStringUtil::endsWith(url, "/im")) - { - return LLTrans::getString("TooltipAgentIM"); - } - if (LLStringUtil::endsWith(url, "/pay")) - { - return LLTrans::getString("TooltipAgentPay"); - } - if (LLStringUtil::endsWith(url, "/offerteleport")) - { - return LLTrans::getString("TooltipAgentOfferTeleport"); - } - if (LLStringUtil::endsWith(url, "/requestfriend")) - { - return LLTrans::getString("TooltipAgentRequestFriend"); - } - return LLTrans::getString("TooltipAgentUrl"); -} - -bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const -{ - std::string url = getUrl(string); - return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect"); -} - -std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - if (!gCacheName) - { - // probably at the login screen, use short string for layout - return LLTrans::getString("LoadingData"); - } - - std::string agent_id_string = getIDStringFromUrl(url); - if (agent_id_string.empty()) - { - // something went wrong, just give raw url - return unescapeUrl(url); - } - - LLUUID agent_id(agent_id_string); - if (agent_id.isNull()) - { - return LLTrans::getString("AvatarNameNobody"); - } - - LLAvatarName av_name; - if (LLAvatarNameCache::get(agent_id, &av_name)) - { - std::string label = av_name.getCompleteName(); - - // handle suffixes like /mute or /offerteleport - label = localize_slapp_label(url, label); - return label; - } - else - { - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); - if (it != mAvatarNameCacheConnections.end()) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - mAvatarNameCacheConnections.erase(it); - } - mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); - - addObserver(agent_id_string, url, cb); - return LLTrans::getString("LoadingData"); - } -} - -LLStyle::Params LLUrlEntryAgent::getStyle() const -{ - LLStyle::Params style_params = LLUrlEntryBase::getStyle(); - style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - return style_params; -} - -std::string localize_slapp_label(const std::string& url, const std::string& full_name) -{ - // customize label string based on agent SLapp suffix - if (LLStringUtil::endsWith(url, "/mute")) - { - return LLTrans::getString("SLappAgentMute") + " " + full_name; - } - if (LLStringUtil::endsWith(url, "/unmute")) - { - return LLTrans::getString("SLappAgentUnmute") + " " + full_name; - } - if (LLStringUtil::endsWith(url, "/im")) - { - return LLTrans::getString("SLappAgentIM") + " " + full_name; - } - if (LLStringUtil::endsWith(url, "/pay")) - { - return LLTrans::getString("SLappAgentPay") + " " + full_name; - } - if (LLStringUtil::endsWith(url, "/offerteleport")) - { - return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name; - } - if (LLStringUtil::endsWith(url, "/requestfriend")) - { - return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name; - } - if (LLStringUtil::endsWith(url, "/removefriend")) - { - return LLTrans::getString("SLappAgentRemoveFriend") + " " + full_name; - } - return full_name; -} - - -std::string LLUrlEntryAgent::getIcon(const std::string &url) -{ - // *NOTE: Could look up a badge here by calling getIDStringFromUrl() - // and looking up the badge for the agent. - return mIcon; -} - -// -// LLUrlEntryAgentName describes a Second Life agent name Url, e.g., -// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) -// -LLUrlEntryAgentName::LLUrlEntryAgentName() -{} - -void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, - const LLAvatarName& av_name) -{ - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); - if (it != mAvatarNameCacheConnections.end()) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - mAvatarNameCacheConnections.erase(it); - } - - std::string label = getName(av_name); - // received the agent name from the server - tell our observers - callObservers(id.asString(), label, mIcon); -} - -std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - if (!gCacheName) - { - // probably at the login screen, use short string for layout - return LLTrans::getString("LoadingData"); - } - - std::string agent_id_string = getIDStringFromUrl(url); - if (agent_id_string.empty()) - { - // something went wrong, just give raw url - return unescapeUrl(url); - } - - LLUUID agent_id(agent_id_string); - if (agent_id.isNull()) - { - return LLTrans::getString("AvatarNameNobody"); - } - - LLAvatarName av_name; - if (LLAvatarNameCache::get(agent_id, &av_name)) - { - return getName(av_name); - } - else - { - avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); - if (it != mAvatarNameCacheConnections.end()) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - mAvatarNameCacheConnections.erase(it); - } - mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); - - addObserver(agent_id_string, url, cb); - return LLTrans::getString("LoadingData"); - } -} - -LLStyle::Params LLUrlEntryAgentName::getStyle() const -{ - // don't override default colors - return LLStyle::Params().is_link(false); -} - -// -// LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g., -// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename -// -LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename", - boost::regex::perl|boost::regex::icase); -} - -std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name) -{ - return avatar_name.getCompleteName(true, true); -} - -// -// LLUrlEntryAgentLegacyName describes a Second Life agent legacy name Url, e.g., -// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname -// -LLUrlEntryAgentLegacyName::LLUrlEntryAgentLegacyName() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/legacyname", - boost::regex::perl|boost::regex::icase); -} - -std::string LLUrlEntryAgentLegacyName::getName(const LLAvatarName& avatar_name) -{ - return avatar_name.getLegacyName(); -} - -// -// LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g., -// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname -// -LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname", - boost::regex::perl|boost::regex::icase); -} - -std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) -{ - return avatar_name.getDisplayName(true); -} - -// -// LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g., -// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username -// -LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username", - boost::regex::perl|boost::regex::icase); -} - -std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) -{ - return avatar_name.getAccountName(); -} - -// -// LLUrlEntryGroup Describes a Second Life group Url, e.g., -// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about -// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect -// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect -// -LLUrlEntryGroup::LLUrlEntryGroup() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_group.xml"; - mIcon = "Generic_Group"; - mTooltip = LLTrans::getString("TooltipGroupUrl"); -} - - - -void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id, - const std::string& name, - bool is_group) -{ - // received the group name from the server - tell our observers - callObservers(id.asString(), name, mIcon); -} - -LLUUID LLUrlEntryGroup::getID(const std::string &string) const -{ - return LLUUID(getIDStringFromUrl(string)); -} - - -std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - if (!gCacheName) - { - // probably at login screen, give something short for layout - return LLTrans::getString("LoadingData"); - } - - std::string group_id_string = getIDStringFromUrl(url); - if (group_id_string.empty()) - { - // something went wrong, give raw url - return unescapeUrl(url); - } - - LLUUID group_id(group_id_string); - std::string group_name; - if (group_id.isNull()) - { - return LLTrans::getString("GroupNameNone"); - } - else if (gCacheName->getGroupName(group_id, group_name)) - { - return group_name; - } - else - { - gCacheName->getGroup(group_id, - boost::bind(&LLUrlEntryGroup::onGroupNameReceived, - this, _1, _2, _3)); - addObserver(group_id_string, url, cb); - return LLTrans::getString("LoadingData"); - } -} - -LLStyle::Params LLUrlEntryGroup::getStyle() const -{ - LLStyle::Params style_params = LLUrlEntryBase::getStyle(); - style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - return style_params; -} - - -// -// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., -// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select -// -LLUrlEntryInventory::LLUrlEntryInventory() -{ - //*TODO: add supporting of inventory item names with whitespaces - //this pattern cann't parse for example - //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value - //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value - mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_inventory.xml"; -} - -std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - std::string label = getStringAfterToken(url, "name="); - return LLURI::unescape(label.empty() ? url : label); -} - -// -// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., -// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 -// -LLUrlEntryObjectIM::LLUrlEntryObjectIM() -{ - mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?\\S*\\w", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_objectim.xml"; -} - -std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - LLURI uri(url); - LLSD query_map = uri.queryMap(); - if (query_map.has("name")) - return query_map["name"]; - return unescapeUrl(url); -} - -std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const -{ - LLURI uri(url); - LLSD query_map = uri.queryMap(); - if (query_map.has("slurl")) - return query_map["slurl"]; - return LLUrlEntryBase::getLocation(url); -} - -// -// LLUrlEntryChat Describes a Second Life chat Url, e.g., -// secondlife:///app/chat/42/This%20Is%20a%20test -// - -LLUrlEntryChat::LLUrlEntryChat() -{ - mPattern = boost::regex("secondlife:///app/chat/\\d+/\\S+", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_slapp.xml"; - mTooltip = LLTrans::getString("TooltipSLAPP"); -} - -std::string LLUrlEntryChat::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - return unescapeUrl(url); -} - -// LLUrlEntryParcel statics. -LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null); -LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null); -LLHost LLUrlEntryParcel::sRegionHost; -bool LLUrlEntryParcel::sDisconnected(false); -std::set LLUrlEntryParcel::sParcelInfoObservers; - -/// -/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., -/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about -/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about -/// -LLUrlEntryParcel::LLUrlEntryParcel() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_parcel.xml"; - mTooltip = LLTrans::getString("TooltipParcelUrl"); - - sParcelInfoObservers.insert(this); -} - -LLUrlEntryParcel::~LLUrlEntryParcel() -{ - sParcelInfoObservers.erase(this); -} - -std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - LLSD path_array = LLURI(url).pathArray(); - S32 path_parts = path_array.size(); - - if (path_parts < 3) // no parcel id - { - LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL; - return url; - } - - std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id - - // Add an observer to call LLUrlLabelCallback when we have parcel name. - addObserver(parcel_id_string, url, cb); - - LLUUID parcel_id(parcel_id_string); - - sendParcelInfoRequest(parcel_id); - - return unescapeUrl(url); -} - -void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id) -{ - if (sRegionHost.isInvalid() || sDisconnected) return; - - LLMessageSystem *msg = gMessageSystem; - msg->newMessage("ParcelInfoRequest"); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, sAgentID ); - msg->addUUID("SessionID", sSessionID); - msg->nextBlock("Data"); - msg->addUUID("ParcelID", parcel_id); - msg->sendReliable(sRegionHost); -} - -void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label) -{ - callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon); -} - -// static -void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) -{ - std::string label(LLStringUtil::null); - if (!parcel_data.name.empty()) - { - label = parcel_data.name; - } - // If parcel name is empty use Sim_name (x, y, z) for parcel label. - else if (!parcel_data.sim_name.empty()) - { - S32 region_x = ll_round(parcel_data.global_x) % REGION_WIDTH_UNITS; - S32 region_y = ll_round(parcel_data.global_y) % REGION_WIDTH_UNITS; - S32 region_z = ll_round(parcel_data.global_z); - - label = llformat("%s (%d, %d, %d)", - parcel_data.sim_name.c_str(), region_x, region_y, region_z); - } - - for (std::set::iterator iter = sParcelInfoObservers.begin(); - iter != sParcelInfoObservers.end(); - ++iter) - { - LLUrlEntryParcel* url_entry = *iter; - if (url_entry) - { - url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label); - } - } -} - -// -// LLUrlEntryPlace Describes secondlife:// URLs -// -LLUrlEntryPlace::LLUrlEntryPlace() -{ - mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_slurl.xml"; - mTooltip = LLTrans::getString("TooltipSLURL"); -} - -std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - // - // we handle SLURLs in the following formats: - // - secondlife://Place/X/Y/Z - // - secondlife://Place/X/Y - // - LLURI uri(url); - std::string location = unescapeUrl(uri.hostName()); - LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); - if (path_parts == 3) - { - // handle slurl with (X,Y,Z) coordinates - std::string x = path_array[0]; - std::string y = path_array[1]; - std::string z = path_array[2]; - return location + " (" + x + "," + y + "," + z + ")"; - } - else if (path_parts == 2) - { - // handle slurl with (X,Y) coordinates - std::string x = path_array[0]; - std::string y = path_array[1]; - return location + " (" + x + "," + y + ")"; - } - - return url; -} - -std::string LLUrlEntryPlace::getLocation(const std::string &url) const -{ - // return the part of the Url after secondlife:// part - return ::getStringAfterToken(url, "://"); -} - -// -// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g. -// secondlife:///app/region/Ahern/128/128/0 -// -LLUrlEntryRegion::LLUrlEntryRegion() -{ - mPattern = boost::regex("secondlife:///app/region/[A-Za-z0-9()_%]+(/\\d+)?(/\\d+)?(/\\d+)?/?", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_slurl.xml"; - mTooltip = LLTrans::getString("TooltipSLURL"); -} - -std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - // - // we handle SLURLs in the following formats: - // - secondlife:///app/region/Place/X/Y/Z - // - secondlife:///app/region/Place/X/Y - // - secondlife:///app/region/Place/X - // - secondlife:///app/region/Place - // - - LLSD path_array = LLURI(url).pathArray(); - S32 path_parts = path_array.size(); - - if (path_parts < 3) // no region name - { - LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL; - return url; - } - - std::string label = unescapeUrl(path_array[2]); // region name - - if (path_parts > 3) // secondlife:///app/region/Place/X - { - std::string x = path_array[3]; - label += " (" + x; - - if (path_parts > 4) // secondlife:///app/region/Place/X/Y - { - std::string y = path_array[4]; - label += "," + y; - - if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z - { - std::string z = path_array[5]; - label = label + "," + z; - } - } - - label += ")"; - } - - return label; -} - -std::string LLUrlEntryRegion::getLocation(const std::string &url) const -{ - LLSD path_array = LLURI(url).pathArray(); - std::string region_name = unescapeUrl(path_array[2]); - return region_name; -} - -// -// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., -// secondlife:///app/teleport/Ahern/50/50/50/ -// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ -// -LLUrlEntryTeleport::LLUrlEntryTeleport() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_teleport.xml"; - mTooltip = LLTrans::getString("TooltipTeleportUrl"); -} - -std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - // - // we handle teleport SLURLs in the following formats: - // - secondlife:///app/teleport/Place/X/Y/Z - // - secondlife:///app/teleport/Place/X/Y - // - secondlife:///app/teleport/Place/X - // - secondlife:///app/teleport/Place - // - LLURI uri(url); - LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); - std::string host = uri.hostName(); - std::string label = LLTrans::getString("SLurlLabelTeleport"); - if (!host.empty()) - { - label += " " + host; - } - if (path_parts == 6) - { - // handle teleport url with (X,Y,Z) coordinates - std::string location = unescapeUrl(path_array[path_parts-4]); - std::string x = path_array[path_parts-3]; - std::string y = path_array[path_parts-2]; - std::string z = path_array[path_parts-1]; - return label + " " + location + " (" + x + "," + y + "," + z + ")"; - } - else if (path_parts == 5) - { - // handle teleport url with (X,Y) coordinates - std::string location = unescapeUrl(path_array[path_parts-3]); - std::string x = path_array[path_parts-2]; - std::string y = path_array[path_parts-1]; - return label + " " + location + " (" + x + "," + y + ")"; - } - else if (path_parts == 4) - { - // handle teleport url with (X) coordinate only - std::string location = unescapeUrl(path_array[path_parts-2]); - std::string x = path_array[path_parts-1]; - return label + " " + location + " (" + x + ")"; - } - else if (path_parts == 3) - { - // handle teleport url with no coordinates - std::string location = unescapeUrl(path_array[path_parts-1]); - return label + " " + location; - } - - return url; -} - -std::string LLUrlEntryTeleport::getLocation(const std::string &url) const -{ - // return the part of the Url after ///app/teleport - return ::getStringAfterToken(url, "app/teleport/"); -} - -// -// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts -// with secondlife:// (used as a catch-all for cases not matched above) -// -LLUrlEntrySL::LLUrlEntrySL() -{ - mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_slapp.xml"; - mTooltip = LLTrans::getString("TooltipSLAPP"); -} - -std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - return unescapeUrl(url); -} - -// -// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts -/// with secondlife:// with the ability to specify a custom label. -// -LLUrlEntrySLLabel::LLUrlEntrySLLabel() -{ - mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_slapp.xml"; - mTooltip = LLTrans::getString("TooltipSLAPP"); -} - -std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - std::string label = getLabelFromWikiLink(url); - return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url); -} - -std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const -{ - return getUrlFromWikiLink(string); -} - -std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const -{ - // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574) - std::string url = getUrl(string); - LLUrlMatch match; - if (LLUrlRegistry::instance().findUrl(url, match)) - { - return match.getTooltip(); - } - - // unrecognized URL? should not happen - return LLUrlEntryBase::getTooltip(string); -} - -bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const -{ - std::string url = getUrl(string); - LLUrlMatch match; - if (LLUrlRegistry::instance().findUrl(url, match)) - { - return match.underlineOnHoverOnly(); - } - - // unrecognized URL? should not happen - return LLUrlEntryBase::underlineOnHoverOnly(string); -} - -// -// LLUrlEntryWorldMap Describes secondlife:/// URLs -// -LLUrlEntryWorldMap::LLUrlEntryWorldMap() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_map.xml"; - mTooltip = LLTrans::getString("TooltipMapUrl"); -} - -std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - // - // we handle SLURLs in the following formats: - // - secondlife:///app/worldmap/PLACE/X/Y/Z - // - secondlife:///app/worldmap/PLACE/X/Y - // - secondlife:///app/worldmap/PLACE/X - // - LLURI uri(url); - LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); - if (path_parts < 3) - { - return url; - } - - const std::string label = LLTrans::getString("SLurlLabelShowOnMap"); - std::string location = unescapeUrl(path_array[2]); - std::string x = (path_parts > 3) ? path_array[3] : "128"; - std::string y = (path_parts > 4) ? path_array[4] : "128"; - std::string z = (path_parts > 5) ? path_array[5] : "0"; - return label + " " + location + " (" + x + "," + y + "," + z + ")"; -} - -std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const -{ - // return the part of the Url after secondlife:///app/worldmap/ part - return ::getStringAfterToken(url, "app/worldmap/"); -} - -// -// LLUrlEntryNoLink lets us turn of URL detection with ... tags -// -LLUrlEntryNoLink::LLUrlEntryNoLink() -{ - mPattern = boost::regex(".*?", - boost::regex::perl|boost::regex::icase); -} - -std::string LLUrlEntryNoLink::getUrl(const std::string &url) const -{ - // return the text between the and tags - return url.substr(8, url.size()-8-9); -} - -std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - return getUrl(url); -} - -LLStyle::Params LLUrlEntryNoLink::getStyle() const -{ - // Don't render as URL (i.e. no context menu or hand cursor). - return LLStyle::Params().is_link(false); -} - - -// -// LLUrlEntryIcon describes an icon with ... tags -// -LLUrlEntryIcon::LLUrlEntryIcon() -{ - mPattern = boost::regex("\\s*([^<]*)?\\s*", - boost::regex::perl|boost::regex::icase); -} - -std::string LLUrlEntryIcon::getUrl(const std::string &url) const -{ - return LLStringUtil::null; -} - -std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - return LLStringUtil::null; -} - -std::string LLUrlEntryIcon::getIcon(const std::string &url) -{ - // Grep icon info between ... tags - // matches[1] contains the icon name/path - boost::match_results matches; - mIcon = (ll_regex_match(url, matches, mPattern) && matches[1].matched) - ? matches[1] - : LLStringUtil::null; - LLStringUtil::trim(mIcon); - return mIcon; -} - -// -// LLUrlEntryEmail Describes a generic mailto: Urls -// -LLUrlEntryEmail::LLUrlEntryEmail() - : LLUrlEntryBase() -{ - mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,63}", - boost::regex::perl | boost::regex::icase); - mMenuName = "menu_url_email.xml"; - mTooltip = LLTrans::getString("TooltipEmail"); -} - -std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - int pos = url.find("mailto:"); - - if (pos == std::string::npos) - { - return escapeUrl(url); - } - - std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8)); - return ret; -} - -std::string LLUrlEntryEmail::getUrl(const std::string &string) const -{ - if (string.find("mailto:") == std::string::npos) - { - return "mailto:" + escapeUrl(string); - } - return escapeUrl(string); -} - -LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile() -{ - mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/profile", - boost::regex::perl|boost::regex::icase); - mIcon = "Generic_Experience"; - mMenuName = "menu_url_experience.xml"; -} - -std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const LLUrlLabelCallback &cb ) -{ - if (!gCacheName) - { - // probably at the login screen, use short string for layout - return LLTrans::getString("LoadingData"); - } - - std::string experience_id_string = getIDStringFromUrl(url); - if (experience_id_string.empty()) - { - // something went wrong, just give raw url - return unescapeUrl(url); - } - - LLUUID experience_id(experience_id_string); - if (experience_id.isNull()) - { - return LLTrans::getString("ExperienceNameNull"); - } - - const LLSD& experience_details = LLExperienceCache::instance().get(experience_id); - if(!experience_details.isUndefined()) - { - std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString(); - return experience_name_string.empty() ? LLTrans::getString("ExperienceNameUntitled") : experience_name_string; - } - - addObserver(experience_id_string, url, cb); - LLExperienceCache::instance().get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1)); - return LLTrans::getString("LoadingData"); - -} - -void LLUrlEntryExperienceProfile::onExperienceDetails( const LLSD& experience_details ) -{ - std::string name = experience_details[LLExperienceCache::NAME].asString(); - if(name.empty()) - { - name = LLTrans::getString("ExperienceNameUntitled"); - } - callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), name, LLStringUtil::null); -} - -// -// LLUrlEntryEmail Describes an IPv6 address -// -LLUrlEntryIPv6::LLUrlEntryIPv6() - : LLUrlEntryBase() -{ - mHostPath = "https?://\\[([a-f0-9:]+:+)+[a-f0-9]+]"; - mPattern = boost::regex(mHostPath + "(:\\d{1,5})?(/\\S*)?", - boost::regex::perl | boost::regex::icase); - mMenuName = "menu_url_http.xml"; - mTooltip = LLTrans::getString("TooltipHttpUrl"); -} - -std::string LLUrlEntryIPv6::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase); - boost::match_results matches; - - if (boost::regex_search(url, matches, regex)) - { - return url.substr(0, matches[0].length()); - } - else - { - return url; - } -} - -std::string LLUrlEntryIPv6::getQuery(const std::string &url) const -{ - boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase); - boost::match_results matches; - - return boost::regex_replace(url, regex, ""); -} - -std::string LLUrlEntryIPv6::getUrl(const std::string &string) const -{ - return string; -} - - -// -// LLUrlEntryKeybinding Displays currently assigned key -// -LLUrlEntryKeybinding::LLUrlEntryKeybinding() - : LLUrlEntryBase() - , pHandler(NULL) -{ - mPattern = boost::regex(APP_HEADER_REGEX "/keybinding/\\w+(\\?mode=\\w+)?$", - boost::regex::perl | boost::regex::icase); - mMenuName = "menu_url_experience.xml"; - - initLocalization(); -} - -std::string LLUrlEntryKeybinding::getLabel(const std::string& url, const LLUrlLabelCallback& cb) -{ - std::string control = getControlName(url); - - std::map::iterator iter = mLocalizations.find(control); - - std::string keybind; - if (pHandler) - { - keybind = pHandler->getKeyBindingAsString(getMode(url), control); - } - - if (iter != mLocalizations.end()) - { - return iter->second.mLocalization + ": " + keybind; - } - - return control + ": " + keybind; -} - -std::string LLUrlEntryKeybinding::getTooltip(const std::string& url) const -{ - std::string control = getControlName(url); - - std::map::const_iterator iter = mLocalizations.find(control); - if (iter != mLocalizations.end()) - { - return iter->second.mTooltip; - } - return url; -} - -std::string LLUrlEntryKeybinding::getControlName(const std::string& url) const -{ - std::string search = "/keybinding/"; - size_t pos_start = url.find(search); - if (pos_start == std::string::npos) - { - return std::string(); - } - pos_start += search.size(); - - size_t pos_end = url.find("?mode="); - if (pos_end == std::string::npos) - { - pos_end = url.size(); - } - return url.substr(pos_start, pos_end - pos_start); -} - -std::string LLUrlEntryKeybinding::getMode(const std::string& url) const -{ - std::string search = "?mode="; - size_t pos_start = url.find(search); - if (pos_start == std::string::npos) - { - return std::string(); - } - pos_start += search.size(); - return url.substr(pos_start, url.size() - pos_start); -} - -void LLUrlEntryKeybinding::initLocalization() -{ - initLocalizationFromFile("control_table_contents_movement.xml"); - initLocalizationFromFile("control_table_contents_camera.xml"); - initLocalizationFromFile("control_table_contents_editing.xml"); - initLocalizationFromFile("control_table_contents_media.xml"); -} - -void LLUrlEntryKeybinding::initLocalizationFromFile(const std::string& filename) -{ - LLXMLNodePtr xmlNode; - LLScrollListCtrl::Contents contents; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) - { - LL_WARNS() << "Failed to load " << filename << LL_ENDL; - return; - } - LLXUIParser parser; - parser.readXUI(xmlNode, contents, filename); - - if (!contents.validateBlock()) - { - LL_WARNS() << "Failed to validate " << filename << LL_ENDL; - return; - } - - for (LLInitParam::ParamIterator::const_iterator row_it = contents.rows.begin(); - row_it != contents.rows.end(); - ++row_it) - { - std::string control = row_it->value.getValue().asString(); - if (!control.empty() && control != "menu_separator") - { - mLocalizations[control] = - LLLocalizationData( - row_it->columns.begin()->value.getValue().asString(), - row_it->columns.begin()->tool_tip.getValue() - ); - } - } -} +/** + * @file llurlentry.cpp + * @author Martin Reddy + * @brief Describes the Url types that can be registered in LLUrlRegistry + * + * $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 "llurlentry.h" +#include "lluictrl.h" +#include "lluri.h" +#include "llurlmatch.h" +#include "llurlregistry.h" +#include "lluriparser.h" + +#include "llavatarnamecache.h" +#include "llcachename.h" +#include "llkeyboard.h" +#include "llregex.h" +#include "llscrolllistctrl.h" // for LLUrlEntryKeybinding file parsing +#include "lltrans.h" +#include "lluicolortable.h" +#include "message.h" +#include "llexperiencecache.h" + +#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" + +// Utility functions +std::string localize_slapp_label(const std::string& url, const std::string& full_name); + + +LLUrlEntryBase::LLUrlEntryBase() +{ +} + +LLUrlEntryBase::~LLUrlEntryBase() +{ +} + +std::string LLUrlEntryBase::getUrl(const std::string &string) const +{ + return escapeUrl(string); +} + +//virtual +std::string LLUrlEntryBase::getIcon(const std::string &url) +{ + return mIcon; +} + +LLStyle::Params LLUrlEntryBase::getStyle() const +{ + LLStyle::Params style_params; + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.font.style = "UNDERLINE"; + return style_params; +} + + +std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const +{ + // return the id from a SLURL in the format /app/{cmd}/{id}/about + LLURI uri(url); + LLSD path_array = uri.pathArray(); + if (path_array.size() == 4) + { + return path_array.get(2).asString(); + } + return ""; +} + +std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const +{ + return LLURI::unescape(url); +} + +std::string LLUrlEntryBase::escapeUrl(const std::string &url) const +{ + static std::string no_escape_chars; + static bool initialized = false; + if (!initialized) + { + no_escape_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~!$?&()*+,@:;=/%#"; + + std::sort(no_escape_chars.begin(), no_escape_chars.end()); + initialized = true; + } + return LLURI::escape(url, no_escape_chars, true); +} + +std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const +{ + // return the label part from [http://www.example.org Label] + const char *text = url.c_str(); + S32 start = 0; + while (! isspace(text[start])) + { + start++; + } + while (text[start] == ' ' || text[start] == '\t') + { + start++; + } + return unescapeUrl(url.substr(start, url.size()-start-1)); +} + +std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const +{ + // return the url part from [http://www.example.org Label] + const char *text = string.c_str(); + S32 end = 0; + while (! isspace(text[end])) + { + end++; + } + return escapeUrl(string.substr(1, end-1)); +} + +void LLUrlEntryBase::addObserver(const std::string &id, + const std::string &url, + const LLUrlLabelCallback &cb) +{ + // add a callback to be notified when we have a label for the uuid + LLUrlEntryObserver observer; + observer.url = url; + observer.signal = new LLUrlLabelSignal(); + if (observer.signal) + { + observer.signal->connect(cb); + mObservers.insert(std::pair(id, observer)); + } +} + +// *NOTE: See also LLUrlEntryAgent::callObservers() +void LLUrlEntryBase::callObservers(const std::string &id, + const std::string &label, + const std::string &icon) +{ + // notify all callbacks waiting on the given uuid + typedef std::multimap::iterator observer_it; + std::pair matching_range = mObservers.equal_range(id); + for (observer_it it = matching_range.first; it != matching_range.second;) + { + // call the callback - give it the new label + LLUrlEntryObserver &observer = it->second; + (*observer.signal)(it->second.url, label, icon); + // then remove the signal - we only need to call it once + delete observer.signal; + mObservers.erase(it++); + } +} + +/// is this a match for a URL that should not be hyperlinked? +bool LLUrlEntryBase::isLinkDisabled() const +{ + // this allows us to have a global setting to turn off text hyperlink highlighting/action + static LLCachedControl globally_disabled(*LLUI::getInstance()->mSettingGroups["config"], "DisableTextHyperlinkActions", false); + + return globally_disabled; +} + +bool LLUrlEntryBase::isWikiLinkCorrect(const std::string &labeled_url) const +{ + LLWString wlabel = utf8str_to_wstring(getLabelFromWikiLink(labeled_url)); + wlabel.erase(std::remove(wlabel.begin(), wlabel.end(), L'\u200B'), wlabel.end()); + + // Unicode URL validation, see SL-15243 + std::replace_if(wlabel.begin(), + wlabel.end(), + [](const llwchar &chr) + { + return (chr == L'\u2024') // "One Dot Leader" + || (chr == L'\uFE52') // "Small Full Stop" + || (chr == L'\uFF0E') // "Fullwidth Full Stop" + // Not a decomposition, but suficiently similar + || (chr == L'\u05C5'); // "Hebrew Mark Lower Dot" + }, + L'\u002E'); // Dot "Full Stop" + + std::replace_if(wlabel.begin(), + wlabel.end(), + [](const llwchar &chr) + { + return (chr == L'\u02D0') // "Modifier Letter Colon" + || (chr == L'\uFF1A') // "Fullwidth Colon" + || (chr == L'\u2236') // "Ratio" + || (chr == L'\uFE55'); // "Small Colon" + }, + L'\u003A'); // Colon + + std::replace_if(wlabel.begin(), + wlabel.end(), + [](const llwchar &chr) + { + return (chr == L'\uFF0F'); // "Fullwidth Solidus" + }, + L'\u002F'); // Solidus + + std::string label = wstring_to_utf8str(wlabel); + if ((label.find(".com") != std::string::npos + || label.find("www.") != std::string::npos) + && label.find("://") == std::string::npos) + { + label = "http://" + label; + } + + return !LLUrlRegistry::instance().hasUrl(label); +} + +std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const +{ + if (url.empty()) + { + return url; + } + LLUriParser up(escapeUrl(url)); + if (up.normalize() == 0) + { + std::string label; + up.extractParts(); + up.glueFirst(label); + + return unescapeUrl(label); + } + return std::string(); +} + +std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const +{ + std::string escaped_url = escapeUrl(url); + LLUriParser up(escaped_url); + + std::string label; + up.extractParts(); + up.glueFirst(label, false); + + size_t pos = escaped_url.find(label); + if (pos == std::string::npos) + { + return ""; + } + pos += label.size(); + return unescapeUrl(escaped_url.substr(pos)); +} + + +static std::string getStringAfterToken(const std::string str, const std::string token) +{ + size_t pos = str.find(token); + if (pos == std::string::npos) + { + return ""; + } + + pos += token.size(); + return str.substr(pos, str.size() - pos); +} + +// +// LLUrlEntryHTTP Describes generic http: and https: Urls +// +LLUrlEntryHTTP::LLUrlEntryHTTP() + : LLUrlEntryBase() +{ + mPattern = boost::regex("https?://([^\\s/?\\.#]+\\.?)+\\.\\w+(:\\d+)?(/\\S*)?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return urlToLabelWithGreyQuery(url); +} + +std::string LLUrlEntryHTTP::getQuery(const std::string &url) const +{ + return urlToGreyQuery(url); +} + +std::string LLUrlEntryHTTP::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "http://" + escapeUrl(string); + } + return escapeUrl(string); +} + +std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const +{ + return unescapeUrl(url); +} + +// +// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label +// We use the wikipedia syntax of [http://www.example.org Text] +// +LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel() +{ + mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + std::string label = getLabelFromWikiLink(url); + return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url); +} + +std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const +{ + return getUrl(string); +} + +std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const +{ + return getUrlFromWikiLink(string); +} + +LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL() + : LLUrlEntryBase() +{ + mPattern = boost::regex("(https?://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryInvalidSLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + + return escapeUrl(url); +} + +std::string LLUrlEntryInvalidSLURL::getUrl(const std::string &string) const +{ + return escapeUrl(string); +} + +std::string LLUrlEntryInvalidSLURL::getTooltip(const std::string &url) const +{ + return unescapeUrl(url); +} + +bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const +{ + S32 actual_parts; + + if(url.find(".com/secondlife/") != std::string::npos) + { + actual_parts = 5; + } + else if(url.find("/app/") != std::string::npos) + { + actual_parts = 6; + } + else + { + actual_parts = 3; + } + + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + S32 x,y,z; + + if (path_parts == actual_parts) + { + // handle slurl with (X,Y,Z) coordinates + LLStringUtil::convertToS32(path_array[path_parts-3],x); + LLStringUtil::convertToS32(path_array[path_parts-2],y); + LLStringUtil::convertToS32(path_array[path_parts-1],z); + + if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0)) + { + return true; + } + } + else if (path_parts == (actual_parts-1)) + { + // handle slurl with (X,Y) coordinates + + LLStringUtil::convertToS32(path_array[path_parts-2],x); + LLStringUtil::convertToS32(path_array[path_parts-1],y); + ; + if((x>= 0 && x<= 256) && (y>= 0 && y<= 256)) + { + return true; + } + } + else if (path_parts == (actual_parts-2)) + { + // handle slurl with (X) coordinate + LLStringUtil::convertToS32(path_array[path_parts-1],x); + if(x>= 0 && x<= 256) + { + return true; + } + } + + return false; +} + +// +// LLUrlEntrySLURL Describes generic http: and https: Urls +// +LLUrlEntrySLURL::LLUrlEntrySLURL() +{ + // see http://slurl.com/about.php for details on the SLURL format + mPattern = boost::regex("https?://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + boost::regex::perl|boost::regex::icase); + mIcon = "Hand"; + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - http://slurl.com/secondlife/Place/X/Y/Z + // - http://slurl.com/secondlife/Place/X/Y + // - http://slurl.com/secondlife/Place/X + // - http://slurl.com/secondlife/Place + // + + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts == 5) + { + // handle slurl with (X,Y,Z) coordinates + std::string location = unescapeUrl(path_array[path_parts-4]); + std::string x = path_array[path_parts-3]; + std::string y = path_array[path_parts-2]; + std::string z = path_array[path_parts-1]; + return location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 4) + { + // handle slurl with (X,Y) coordinates + std::string location = unescapeUrl(path_array[path_parts-3]); + std::string x = path_array[path_parts-2]; + std::string y = path_array[path_parts-1]; + return location + " (" + x + "," + y + ")"; + } + else if (path_parts == 3) + { + // handle slurl with (X) coordinate + std::string location = unescapeUrl(path_array[path_parts-2]); + std::string x = path_array[path_parts-1]; + return location + " (" + x + ")"; + } + else if (path_parts == 2) + { + // handle slurl with no coordinates + std::string location = unescapeUrl(path_array[path_parts-1]); + return location; + } + + return url; +} + +std::string LLUrlEntrySLURL::getLocation(const std::string &url) const +{ + // return the part of the Url after slurl.com/secondlife/ + const std::string search_string = "/secondlife"; + size_t pos = url.find(search_string); + if (pos == std::string::npos) + { + return ""; + } + + pos += search_string.size() + 1; + return url.substr(pos, url.size() - pos); +} + +// +// LLUrlEntrySeconlifeURL Describes *secondlife.com/ *lindenlab.com/ *secondlifegrid.net/ and *tilia-inc.com/ urls to substitute icon 'hand.png' before link +// +LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL() +{ + mPattern = boost::regex("((http://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com)" + "|" + "(http://([-\\w\\.]*\\.)?secondlifegrid\\.net)" + "|" + "(https://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(:\\d{1,5})?)" + "|" + "(https://([-\\w\\.]*\\.)?secondlifegrid\\.net(:\\d{1,5})?)" + "|" + "(https?://([-\\w\\.]*\\.)?secondlife\\.io(:\\d{1,5})?))" + "\\/\\S*", + boost::regex::perl|boost::regex::icase); + + mIcon = "Hand"; + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +/// Return the url from a string that matched the regex +std::string LLUrlEntrySecondlifeURL::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "https://" + escapeUrl(string); + } + return escapeUrl(string); +} + +std::string LLUrlEntrySecondlifeURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return urlToLabelWithGreyQuery(url); +} + +std::string LLUrlEntrySecondlifeURL::getQuery(const std::string &url) const +{ + return urlToGreyQuery(url); +} + +std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const +{ + return url; +} + +// +// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com *lindenlab.com *secondlifegrid.net and *tilia-inc.com urls to substitute icon 'hand.png' before link +// +LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() + { + mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(?!\\S)" + "|" + "https?://([-\\w\\.]*\\.)?secondlifegrid\\.net(?!\\S)", + boost::regex::perl|boost::regex::icase); + + mIcon = "Hand"; + mMenuName = "menu_url_http.xml"; +} + +// +// LLUrlEntryAgent Describes a Second Life agent Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// +LLUrlEntryAgent::LLUrlEntryAgent() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_agent.xml"; + mIcon = "Generic_Person"; +} + +// virtual +void LLUrlEntryAgent::callObservers(const std::string &id, + const std::string &label, + const std::string &icon) +{ + // notify all callbacks waiting on the given uuid + typedef std::multimap::iterator observer_it; + std::pair matching_range = mObservers.equal_range(id); + for (observer_it it = matching_range.first; it != matching_range.second;) + { + // call the callback - give it the new label + LLUrlEntryObserver &observer = it->second; + std::string final_label = localize_slapp_label(observer.url, label); + (*observer.signal)(observer.url, final_label, icon); + // then remove the signal - we only need to call it once + delete observer.signal; + mObservers.erase(it++); + } +} + +void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, + const LLAvatarName& av_name) +{ + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); + if (it != mAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); + } + + std::string label = av_name.getCompleteName(); + + // received the agent name from the server - tell our observers + callObservers(id.asString(), label, mIcon); +} + +LLUUID LLUrlEntryAgent::getID(const std::string &string) const +{ + return LLUUID(getIDStringFromUrl(string)); +} + +std::string LLUrlEntryAgent::getTooltip(const std::string &string) const +{ + // return a tooltip corresponding to the URL type instead of the generic one + std::string url = getUrl(string); + + if (LLStringUtil::endsWith(url, "/inspect")) + { + return LLTrans::getString("TooltipAgentInspect"); + } + if (LLStringUtil::endsWith(url, "/mute")) + { + return LLTrans::getString("TooltipAgentMute"); + } + if (LLStringUtil::endsWith(url, "/unmute")) + { + return LLTrans::getString("TooltipAgentUnmute"); + } + if (LLStringUtil::endsWith(url, "/im")) + { + return LLTrans::getString("TooltipAgentIM"); + } + if (LLStringUtil::endsWith(url, "/pay")) + { + return LLTrans::getString("TooltipAgentPay"); + } + if (LLStringUtil::endsWith(url, "/offerteleport")) + { + return LLTrans::getString("TooltipAgentOfferTeleport"); + } + if (LLStringUtil::endsWith(url, "/requestfriend")) + { + return LLTrans::getString("TooltipAgentRequestFriend"); + } + return LLTrans::getString("TooltipAgentUrl"); +} + +bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const +{ + std::string url = getUrl(string); + return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect"); +} + +std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at the login screen, use short string for layout + return LLTrans::getString("LoadingData"); + } + + std::string agent_id_string = getIDStringFromUrl(url); + if (agent_id_string.empty()) + { + // something went wrong, just give raw url + return unescapeUrl(url); + } + + LLUUID agent_id(agent_id_string); + if (agent_id.isNull()) + { + return LLTrans::getString("AvatarNameNobody"); + } + + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_id, &av_name)) + { + std::string label = av_name.getCompleteName(); + + // handle suffixes like /mute or /offerteleport + label = localize_slapp_label(url, label); + return label; + } + else + { + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); + if (it != mAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); + } + mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + + addObserver(agent_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +LLStyle::Params LLUrlEntryAgent::getStyle() const +{ + LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + return style_params; +} + +std::string localize_slapp_label(const std::string& url, const std::string& full_name) +{ + // customize label string based on agent SLapp suffix + if (LLStringUtil::endsWith(url, "/mute")) + { + return LLTrans::getString("SLappAgentMute") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/unmute")) + { + return LLTrans::getString("SLappAgentUnmute") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/im")) + { + return LLTrans::getString("SLappAgentIM") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/pay")) + { + return LLTrans::getString("SLappAgentPay") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/offerteleport")) + { + return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/requestfriend")) + { + return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/removefriend")) + { + return LLTrans::getString("SLappAgentRemoveFriend") + " " + full_name; + } + return full_name; +} + + +std::string LLUrlEntryAgent::getIcon(const std::string &url) +{ + // *NOTE: Could look up a badge here by calling getIDStringFromUrl() + // and looking up the badge for the agent. + return mIcon; +} + +// +// LLUrlEntryAgentName describes a Second Life agent name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// +LLUrlEntryAgentName::LLUrlEntryAgentName() +{} + +void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, + const LLAvatarName& av_name) +{ + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); + if (it != mAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); + } + + std::string label = getName(av_name); + // received the agent name from the server - tell our observers + callObservers(id.asString(), label, mIcon); +} + +std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at the login screen, use short string for layout + return LLTrans::getString("LoadingData"); + } + + std::string agent_id_string = getIDStringFromUrl(url); + if (agent_id_string.empty()) + { + // something went wrong, just give raw url + return unescapeUrl(url); + } + + LLUUID agent_id(agent_id_string); + if (agent_id.isNull()) + { + return LLTrans::getString("AvatarNameNobody"); + } + + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_id, &av_name)) + { + return getName(av_name); + } + else + { + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); + if (it != mAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); + } + mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + + addObserver(agent_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +LLStyle::Params LLUrlEntryAgentName::getStyle() const +{ + // don't override default colors + return LLStyle::Params().is_link(false); +} + +// +// LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// +LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getCompleteName(true, true); +} + +// +// LLUrlEntryAgentLegacyName describes a Second Life agent legacy name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname +// +LLUrlEntryAgentLegacyName::LLUrlEntryAgentLegacyName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/legacyname", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentLegacyName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getLegacyName(); +} + +// +// LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// +LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getDisplayName(true); +} + +// +// LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// +LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getAccountName(); +} + +// +// LLUrlEntryGroup Describes a Second Life group Url, e.g., +// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about +// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// +LLUrlEntryGroup::LLUrlEntryGroup() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_group.xml"; + mIcon = "Generic_Group"; + mTooltip = LLTrans::getString("TooltipGroupUrl"); +} + + + +void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id, + const std::string& name, + bool is_group) +{ + // received the group name from the server - tell our observers + callObservers(id.asString(), name, mIcon); +} + +LLUUID LLUrlEntryGroup::getID(const std::string &string) const +{ + return LLUUID(getIDStringFromUrl(string)); +} + + +std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at login screen, give something short for layout + return LLTrans::getString("LoadingData"); + } + + std::string group_id_string = getIDStringFromUrl(url); + if (group_id_string.empty()) + { + // something went wrong, give raw url + return unescapeUrl(url); + } + + LLUUID group_id(group_id_string); + std::string group_name; + if (group_id.isNull()) + { + return LLTrans::getString("GroupNameNone"); + } + else if (gCacheName->getGroupName(group_id, group_name)) + { + return group_name; + } + else + { + gCacheName->getGroup(group_id, + boost::bind(&LLUrlEntryGroup::onGroupNameReceived, + this, _1, _2, _3)); + addObserver(group_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +LLStyle::Params LLUrlEntryGroup::getStyle() const +{ + LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + return style_params; +} + + +// +// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., +// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select +// +LLUrlEntryInventory::LLUrlEntryInventory() +{ + //*TODO: add supporting of inventory item names with whitespaces + //this pattern cann't parse for example + //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_inventory.xml"; +} + +std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + std::string label = getStringAfterToken(url, "name="); + return LLURI::unescape(label.empty() ? url : label); +} + +// +// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., +// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 +// +LLUrlEntryObjectIM::LLUrlEntryObjectIM() +{ + mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?\\S*\\w", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_objectim.xml"; +} + +std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + if (query_map.has("name")) + return query_map["name"]; + return unescapeUrl(url); +} + +std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + if (query_map.has("slurl")) + return query_map["slurl"]; + return LLUrlEntryBase::getLocation(url); +} + +// +// LLUrlEntryChat Describes a Second Life chat Url, e.g., +// secondlife:///app/chat/42/This%20Is%20a%20test +// + +LLUrlEntryChat::LLUrlEntryChat() +{ + mPattern = boost::regex("secondlife:///app/chat/\\d+/\\S+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slapp.xml"; + mTooltip = LLTrans::getString("TooltipSLAPP"); +} + +std::string LLUrlEntryChat::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +// LLUrlEntryParcel statics. +LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null); +LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null); +LLHost LLUrlEntryParcel::sRegionHost; +bool LLUrlEntryParcel::sDisconnected(false); +std::set LLUrlEntryParcel::sParcelInfoObservers; + +/// +/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., +/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// +LLUrlEntryParcel::LLUrlEntryParcel() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_parcel.xml"; + mTooltip = LLTrans::getString("TooltipParcelUrl"); + + sParcelInfoObservers.insert(this); +} + +LLUrlEntryParcel::~LLUrlEntryParcel() +{ + sParcelInfoObservers.erase(this); +} + +std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no parcel id + { + LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL; + return url; + } + + std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id + + // Add an observer to call LLUrlLabelCallback when we have parcel name. + addObserver(parcel_id_string, url, cb); + + LLUUID parcel_id(parcel_id_string); + + sendParcelInfoRequest(parcel_id); + + return unescapeUrl(url); +} + +void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id) +{ + if (sRegionHost.isInvalid() || sDisconnected) return; + + LLMessageSystem *msg = gMessageSystem; + msg->newMessage("ParcelInfoRequest"); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, sAgentID ); + msg->addUUID("SessionID", sSessionID); + msg->nextBlock("Data"); + msg->addUUID("ParcelID", parcel_id); + msg->sendReliable(sRegionHost); +} + +void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label) +{ + callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon); +} + +// static +void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) +{ + std::string label(LLStringUtil::null); + if (!parcel_data.name.empty()) + { + label = parcel_data.name; + } + // If parcel name is empty use Sim_name (x, y, z) for parcel label. + else if (!parcel_data.sim_name.empty()) + { + S32 region_x = ll_round(parcel_data.global_x) % REGION_WIDTH_UNITS; + S32 region_y = ll_round(parcel_data.global_y) % REGION_WIDTH_UNITS; + S32 region_z = ll_round(parcel_data.global_z); + + label = llformat("%s (%d, %d, %d)", + parcel_data.sim_name.c_str(), region_x, region_y, region_z); + } + + for (std::set::iterator iter = sParcelInfoObservers.begin(); + iter != sParcelInfoObservers.end(); + ++iter) + { + LLUrlEntryParcel* url_entry = *iter; + if (url_entry) + { + url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label); + } + } +} + +// +// LLUrlEntryPlace Describes secondlife:// URLs +// +LLUrlEntryPlace::LLUrlEntryPlace() +{ + mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife://Place/X/Y/Z + // - secondlife://Place/X/Y + // + LLURI uri(url); + std::string location = unescapeUrl(uri.hostName()); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts == 3) + { + // handle slurl with (X,Y,Z) coordinates + std::string x = path_array[0]; + std::string y = path_array[1]; + std::string z = path_array[2]; + return location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 2) + { + // handle slurl with (X,Y) coordinates + std::string x = path_array[0]; + std::string y = path_array[1]; + return location + " (" + x + "," + y + ")"; + } + + return url; +} + +std::string LLUrlEntryPlace::getLocation(const std::string &url) const +{ + // return the part of the Url after secondlife:// part + return ::getStringAfterToken(url, "://"); +} + +// +// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g. +// secondlife:///app/region/Ahern/128/128/0 +// +LLUrlEntryRegion::LLUrlEntryRegion() +{ + mPattern = boost::regex("secondlife:///app/region/[A-Za-z0-9()_%]+(/\\d+)?(/\\d+)?(/\\d+)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/region/Place/X/Y/Z + // - secondlife:///app/region/Place/X/Y + // - secondlife:///app/region/Place/X + // - secondlife:///app/region/Place + // + + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no region name + { + LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL; + return url; + } + + std::string label = unescapeUrl(path_array[2]); // region name + + if (path_parts > 3) // secondlife:///app/region/Place/X + { + std::string x = path_array[3]; + label += " (" + x; + + if (path_parts > 4) // secondlife:///app/region/Place/X/Y + { + std::string y = path_array[4]; + label += "," + y; + + if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z + { + std::string z = path_array[5]; + label = label + "," + z; + } + } + + label += ")"; + } + + return label; +} + +std::string LLUrlEntryRegion::getLocation(const std::string &url) const +{ + LLSD path_array = LLURI(url).pathArray(); + std::string region_name = unescapeUrl(path_array[2]); + return region_name; +} + +// +// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., +// secondlife:///app/teleport/Ahern/50/50/50/ +// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ +// +LLUrlEntryTeleport::LLUrlEntryTeleport() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_teleport.xml"; + mTooltip = LLTrans::getString("TooltipTeleportUrl"); +} + +std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle teleport SLURLs in the following formats: + // - secondlife:///app/teleport/Place/X/Y/Z + // - secondlife:///app/teleport/Place/X/Y + // - secondlife:///app/teleport/Place/X + // - secondlife:///app/teleport/Place + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + std::string host = uri.hostName(); + std::string label = LLTrans::getString("SLurlLabelTeleport"); + if (!host.empty()) + { + label += " " + host; + } + if (path_parts == 6) + { + // handle teleport url with (X,Y,Z) coordinates + std::string location = unescapeUrl(path_array[path_parts-4]); + std::string x = path_array[path_parts-3]; + std::string y = path_array[path_parts-2]; + std::string z = path_array[path_parts-1]; + return label + " " + location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 5) + { + // handle teleport url with (X,Y) coordinates + std::string location = unescapeUrl(path_array[path_parts-3]); + std::string x = path_array[path_parts-2]; + std::string y = path_array[path_parts-1]; + return label + " " + location + " (" + x + "," + y + ")"; + } + else if (path_parts == 4) + { + // handle teleport url with (X) coordinate only + std::string location = unescapeUrl(path_array[path_parts-2]); + std::string x = path_array[path_parts-1]; + return label + " " + location + " (" + x + ")"; + } + else if (path_parts == 3) + { + // handle teleport url with no coordinates + std::string location = unescapeUrl(path_array[path_parts-1]); + return label + " " + location; + } + + return url; +} + +std::string LLUrlEntryTeleport::getLocation(const std::string &url) const +{ + // return the part of the Url after ///app/teleport + return ::getStringAfterToken(url, "app/teleport/"); +} + +// +// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts +// with secondlife:// (used as a catch-all for cases not matched above) +// +LLUrlEntrySL::LLUrlEntrySL() +{ + mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slapp.xml"; + mTooltip = LLTrans::getString("TooltipSLAPP"); +} + +std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +// +// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// with the ability to specify a custom label. +// +LLUrlEntrySLLabel::LLUrlEntrySLLabel() +{ + mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slapp.xml"; + mTooltip = LLTrans::getString("TooltipSLAPP"); +} + +std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + std::string label = getLabelFromWikiLink(url); + return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url); +} + +std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const +{ + return getUrlFromWikiLink(string); +} + +std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const +{ + // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574) + std::string url = getUrl(string); + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + return match.getTooltip(); + } + + // unrecognized URL? should not happen + return LLUrlEntryBase::getTooltip(string); +} + +bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const +{ + std::string url = getUrl(string); + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + return match.underlineOnHoverOnly(); + } + + // unrecognized URL? should not happen + return LLUrlEntryBase::underlineOnHoverOnly(string); +} + +// +// LLUrlEntryWorldMap Describes secondlife:/// URLs +// +LLUrlEntryWorldMap::LLUrlEntryWorldMap() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_map.xml"; + mTooltip = LLTrans::getString("TooltipMapUrl"); +} + +std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/worldmap/PLACE/X/Y/Z + // - secondlife:///app/worldmap/PLACE/X/Y + // - secondlife:///app/worldmap/PLACE/X + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts < 3) + { + return url; + } + + const std::string label = LLTrans::getString("SLurlLabelShowOnMap"); + std::string location = unescapeUrl(path_array[2]); + std::string x = (path_parts > 3) ? path_array[3] : "128"; + std::string y = (path_parts > 4) ? path_array[4] : "128"; + std::string z = (path_parts > 5) ? path_array[5] : "0"; + return label + " " + location + " (" + x + "," + y + "," + z + ")"; +} + +std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const +{ + // return the part of the Url after secondlife:///app/worldmap/ part + return ::getStringAfterToken(url, "app/worldmap/"); +} + +// +// LLUrlEntryNoLink lets us turn of URL detection with ... tags +// +LLUrlEntryNoLink::LLUrlEntryNoLink() +{ + mPattern = boost::regex(".*?", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryNoLink::getUrl(const std::string &url) const +{ + // return the text between the and tags + return url.substr(8, url.size()-8-9); +} + +std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return getUrl(url); +} + +LLStyle::Params LLUrlEntryNoLink::getStyle() const +{ + // Don't render as URL (i.e. no context menu or hand cursor). + return LLStyle::Params().is_link(false); +} + + +// +// LLUrlEntryIcon describes an icon with ... tags +// +LLUrlEntryIcon::LLUrlEntryIcon() +{ + mPattern = boost::regex("\\s*([^<]*)?\\s*", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryIcon::getUrl(const std::string &url) const +{ + return LLStringUtil::null; +} + +std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return LLStringUtil::null; +} + +std::string LLUrlEntryIcon::getIcon(const std::string &url) +{ + // Grep icon info between ... tags + // matches[1] contains the icon name/path + boost::match_results matches; + mIcon = (ll_regex_match(url, matches, mPattern) && matches[1].matched) + ? matches[1] + : LLStringUtil::null; + LLStringUtil::trim(mIcon); + return mIcon; +} + +// +// LLUrlEntryEmail Describes a generic mailto: Urls +// +LLUrlEntryEmail::LLUrlEntryEmail() + : LLUrlEntryBase() +{ + mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,63}", + boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_email.xml"; + mTooltip = LLTrans::getString("TooltipEmail"); +} + +std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + int pos = url.find("mailto:"); + + if (pos == std::string::npos) + { + return escapeUrl(url); + } + + std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8)); + return ret; +} + +std::string LLUrlEntryEmail::getUrl(const std::string &string) const +{ + if (string.find("mailto:") == std::string::npos) + { + return "mailto:" + escapeUrl(string); + } + return escapeUrl(string); +} + +LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/profile", + boost::regex::perl|boost::regex::icase); + mIcon = "Generic_Experience"; + mMenuName = "menu_url_experience.xml"; +} + +std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const LLUrlLabelCallback &cb ) +{ + if (!gCacheName) + { + // probably at the login screen, use short string for layout + return LLTrans::getString("LoadingData"); + } + + std::string experience_id_string = getIDStringFromUrl(url); + if (experience_id_string.empty()) + { + // something went wrong, just give raw url + return unescapeUrl(url); + } + + LLUUID experience_id(experience_id_string); + if (experience_id.isNull()) + { + return LLTrans::getString("ExperienceNameNull"); + } + + const LLSD& experience_details = LLExperienceCache::instance().get(experience_id); + if(!experience_details.isUndefined()) + { + std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString(); + return experience_name_string.empty() ? LLTrans::getString("ExperienceNameUntitled") : experience_name_string; + } + + addObserver(experience_id_string, url, cb); + LLExperienceCache::instance().get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1)); + return LLTrans::getString("LoadingData"); + +} + +void LLUrlEntryExperienceProfile::onExperienceDetails( const LLSD& experience_details ) +{ + std::string name = experience_details[LLExperienceCache::NAME].asString(); + if(name.empty()) + { + name = LLTrans::getString("ExperienceNameUntitled"); + } + callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), name, LLStringUtil::null); +} + +// +// LLUrlEntryEmail Describes an IPv6 address +// +LLUrlEntryIPv6::LLUrlEntryIPv6() + : LLUrlEntryBase() +{ + mHostPath = "https?://\\[([a-f0-9:]+:+)+[a-f0-9]+]"; + mPattern = boost::regex(mHostPath + "(:\\d{1,5})?(/\\S*)?", + boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryIPv6::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase); + boost::match_results matches; + + if (boost::regex_search(url, matches, regex)) + { + return url.substr(0, matches[0].length()); + } + else + { + return url; + } +} + +std::string LLUrlEntryIPv6::getQuery(const std::string &url) const +{ + boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase); + boost::match_results matches; + + return boost::regex_replace(url, regex, ""); +} + +std::string LLUrlEntryIPv6::getUrl(const std::string &string) const +{ + return string; +} + + +// +// LLUrlEntryKeybinding Displays currently assigned key +// +LLUrlEntryKeybinding::LLUrlEntryKeybinding() + : LLUrlEntryBase() + , pHandler(NULL) +{ + mPattern = boost::regex(APP_HEADER_REGEX "/keybinding/\\w+(\\?mode=\\w+)?$", + boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_experience.xml"; + + initLocalization(); +} + +std::string LLUrlEntryKeybinding::getLabel(const std::string& url, const LLUrlLabelCallback& cb) +{ + std::string control = getControlName(url); + + std::map::iterator iter = mLocalizations.find(control); + + std::string keybind; + if (pHandler) + { + keybind = pHandler->getKeyBindingAsString(getMode(url), control); + } + + if (iter != mLocalizations.end()) + { + return iter->second.mLocalization + ": " + keybind; + } + + return control + ": " + keybind; +} + +std::string LLUrlEntryKeybinding::getTooltip(const std::string& url) const +{ + std::string control = getControlName(url); + + std::map::const_iterator iter = mLocalizations.find(control); + if (iter != mLocalizations.end()) + { + return iter->second.mTooltip; + } + return url; +} + +std::string LLUrlEntryKeybinding::getControlName(const std::string& url) const +{ + std::string search = "/keybinding/"; + size_t pos_start = url.find(search); + if (pos_start == std::string::npos) + { + return std::string(); + } + pos_start += search.size(); + + size_t pos_end = url.find("?mode="); + if (pos_end == std::string::npos) + { + pos_end = url.size(); + } + return url.substr(pos_start, pos_end - pos_start); +} + +std::string LLUrlEntryKeybinding::getMode(const std::string& url) const +{ + std::string search = "?mode="; + size_t pos_start = url.find(search); + if (pos_start == std::string::npos) + { + return std::string(); + } + pos_start += search.size(); + return url.substr(pos_start, url.size() - pos_start); +} + +void LLUrlEntryKeybinding::initLocalization() +{ + initLocalizationFromFile("control_table_contents_movement.xml"); + initLocalizationFromFile("control_table_contents_camera.xml"); + initLocalizationFromFile("control_table_contents_editing.xml"); + initLocalizationFromFile("control_table_contents_media.xml"); +} + +void LLUrlEntryKeybinding::initLocalizationFromFile(const std::string& filename) +{ + LLXMLNodePtr xmlNode; + LLScrollListCtrl::Contents contents; + if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + { + LL_WARNS() << "Failed to load " << filename << LL_ENDL; + return; + } + LLXUIParser parser; + parser.readXUI(xmlNode, contents, filename); + + if (!contents.validateBlock()) + { + LL_WARNS() << "Failed to validate " << filename << LL_ENDL; + return; + } + + for (LLInitParam::ParamIterator::const_iterator row_it = contents.rows.begin(); + row_it != contents.rows.end(); + ++row_it) + { + std::string control = row_it->value.getValue().asString(); + if (!control.empty() && control != "menu_separator") + { + mLocalizations[control] = + LLLocalizationData( + row_it->columns.begin()->value.getValue().asString(), + row_it->columns.begin()->tool_tip.getValue() + ); + } + } +} diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 463a97afb0..84ff278942 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -1,586 +1,586 @@ -/** - * @file llurlentry.h - * @author Martin Reddy - * @brief Describes the Url types that can be registered in LLUrlRegistry - * - * $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_LLURLENTRY_H -#define LL_LLURLENTRY_H - -#include "lluuid.h" -#include "lluicolor.h" -#include "llstyle.h" - -#include "llavatarname.h" -#include "llhost.h" // for resolving parcel name by parcel id - -#include -#include -#include -#include - -class LLAvatarName; - -typedef boost::signals2::signal LLUrlLabelSignal; -typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback; - -/// -/// LLUrlEntryBase is the base class of all Url types registered in the -/// LLUrlRegistry. Each derived classes provides a regular expression -/// to match the Url type (e.g., http://... or secondlife://...) along -/// with an optional icon to display next to instances of the Url in -/// a text display and a XUI file to use for any context menu popup. -/// Functions are also provided to compute an appropriate label and -/// tooltip/status bar text for the Url. -/// -/// Some derived classes of LLUrlEntryBase may wish to compute an -/// appropriate label for a Url by asking the server for information. -/// You must therefore provide a callback method, so that you can be -/// notified when an updated label has been received from the server. -/// This label should then be used to replace any previous label -/// that you received from getLabel() for the Url in question. -/// -class LLUrlEntryBase -{ -public: - LLUrlEntryBase(); - virtual ~LLUrlEntryBase(); - - /// Return the regex pattern that matches this Url - boost::regex getPattern() const { return mPattern; } - - /// Return the url from a string that matched the regex - virtual std::string getUrl(const std::string &string) const; - - /// Given a matched Url, return a label for the Url - virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; } - - /// Return port, query and fragment parts for the Url - virtual std::string getQuery(const std::string &url) const { return ""; } - - /// Return an icon that can be displayed next to Urls of this type - virtual std::string getIcon(const std::string &url); - - /// Return the style to render the displayed text - virtual LLStyle::Params getStyle() const; - - /// Given a matched Url, return a tooltip string for the hyperlink - virtual std::string getTooltip(const std::string &string) const { return mTooltip; } - - /// Return the name of a XUI file containing the context menu items - std::string getMenuName() const { return mMenuName; } - - /// Return the name of a SL location described by this Url, if any - virtual std::string getLocation(const std::string &url) const { return ""; } - - /// Should this link text be underlined only when mouse is hovered over it? - virtual bool underlineOnHoverOnly(const std::string &string) const { return false; } - - virtual bool isTrusted() const { return false; } - - virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } - - bool isLinkDisabled() const; - - bool isWikiLinkCorrect(const std::string &url) const; - - virtual bool isSLURLvalid(const std::string &url) const { return true; }; - -protected: - std::string getIDStringFromUrl(const std::string &url) const; - std::string escapeUrl(const std::string &url) const; - std::string unescapeUrl(const std::string &url) const; - std::string getLabelFromWikiLink(const std::string &url) const; - std::string getUrlFromWikiLink(const std::string &string) const; - void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb); - std::string urlToLabelWithGreyQuery(const std::string &url) const; - std::string urlToGreyQuery(const std::string &url) const; - virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon); - - typedef struct { - std::string url; - LLUrlLabelSignal *signal; - } LLUrlEntryObserver; - - boost::regex mPattern; - std::string mIcon; - std::string mMenuName; - std::string mTooltip; - std::multimap mObservers; -}; - -/// -/// LLUrlEntryHTTP Describes generic http: and https: Urls -/// -class LLUrlEntryHTTP : public LLUrlEntryBase -{ -public: - LLUrlEntryHTTP(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getQuery(const std::string &url) const; - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getTooltip(const std::string &url) const; -}; - -/// -/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels -/// -class LLUrlEntryHTTPLabel : public LLUrlEntryBase -{ -public: - LLUrlEntryHTTPLabel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ std::string getUrl(const std::string &string) const; -}; - -class LLUrlEntryInvalidSLURL : public LLUrlEntryBase -{ -public: - LLUrlEntryInvalidSLURL(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getTooltip(const std::string &url) const; - - bool isSLURLvalid(const std::string &url) const; -}; - -/// -/// LLUrlEntrySLURL Describes http://slurl.com/... Urls -/// -class LLUrlEntrySLURL : public LLUrlEntryBase -{ -public: - LLUrlEntrySLURL(); - /*virtual*/ bool isTrusted() const { return true; } - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; -}; - -/// -/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls -/// -class LLUrlEntrySecondlifeURL : public LLUrlEntryBase -{ -public: - LLUrlEntrySecondlifeURL(); - /*virtual*/ bool isTrusted() const { return true; } - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getQuery(const std::string &url) const; - /*virtual*/ std::string getTooltip(const std::string &url) const; -}; - -/// -/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls -/// -class LLUrlEntrySimpleSecondlifeURL : public LLUrlEntrySecondlifeURL -{ -public: - LLUrlEntrySimpleSecondlifeURL(); -}; - -/// -/// LLUrlEntryAgent Describes a Second Life agent Url, e.g., -/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about -class LLUrlEntryAgent : public LLUrlEntryBase -{ -public: - LLUrlEntryAgent(); - ~LLUrlEntryAgent() - { - for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - } - mAvatarNameCacheConnections.clear(); - } - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getIcon(const std::string &url); - /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ LLStyle::Params getStyle() const; - /*virtual*/ LLUUID getID(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; -protected: - /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); -private: - void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - - typedef std::map avatar_name_cache_connection_map_t; - avatar_name_cache_connection_map_t mAvatarNameCacheConnections; -}; - -/// -/// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g., -/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) -/// that displays various forms of user name -/// This is a base class for the various implementations of name display -class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable -{ -public: - LLUrlEntryAgentName(); - ~LLUrlEntryAgentName() - { - for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) - { - if (it->second.connected()) - { - it->second.disconnect(); - } - } - mAvatarNameCacheConnections.clear(); - } - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ LLStyle::Params getStyle() const; -protected: - // override this to pull out relevant name fields - virtual std::string getName(const LLAvatarName& avatar_name) = 0; -private: - void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - - typedef std::map avatar_name_cache_connection_map_t; - avatar_name_cache_connection_map_t mAvatarNameCacheConnections; -}; - - -/// -/// LLUrlEntryAgentCompleteName Describes a Second Life agent name Url, e.g., -/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename -/// that displays the full display name + user name for an avatar -/// such as "James Linden (james.linden)" -class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName -{ -public: - LLUrlEntryAgentCompleteName(); -private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); -}; - -class LLUrlEntryAgentLegacyName : public LLUrlEntryAgentName -{ -public: - LLUrlEntryAgentLegacyName(); -private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); -}; - -/// -/// LLUrlEntryAgentDisplayName Describes a Second Life agent display name Url, e.g., -/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname -/// that displays the just the display name for an avatar -/// such as "James Linden" -class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName -{ -public: - LLUrlEntryAgentDisplayName(); -private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); -}; - -/// -/// LLUrlEntryAgentUserName Describes a Second Life agent username Url, e.g., -/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username -/// that displays the just the display name for an avatar -/// such as "james.linden" -class LLUrlEntryAgentUserName : public LLUrlEntryAgentName -{ -public: - LLUrlEntryAgentUserName(); -private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); -}; - -/// -/// LLUrlEntryExperienceProfile Describes a Second Life experience profile Url, e.g., -/// secondlife:///app/experience/0e346d8b-4433-4d66-a6b0-fd37083abc4c/profile -/// that displays the experience name -class LLUrlEntryExperienceProfile : public LLUrlEntryBase -{ -public: - LLUrlEntryExperienceProfile(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); -private: - void onExperienceDetails(const LLSD& experience_details); -}; - - -/// -/// LLUrlEntryGroup Describes a Second Life group Url, e.g., -/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about -/// -class LLUrlEntryGroup : public LLUrlEntryBase -{ -public: - LLUrlEntryGroup(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ LLStyle::Params getStyle() const; - /*virtual*/ LLUUID getID(const std::string &string) const; -private: - void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); -}; - -/// -/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., -/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select -/// -class LLUrlEntryInventory : public LLUrlEntryBase -{ -public: - LLUrlEntryInventory(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); -private: -}; - -/// -/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., -/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 -/// -class LLUrlEntryObjectIM : public LLUrlEntryBase -{ -public: - LLUrlEntryObjectIM(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; -private: -}; - -// -// LLUrlEntryChat Describes a Second Life chat Url, e.g., -// secondlife:///app/chat/42/This%20Is%20a%20test -// -class LLUrlEntryChat : public LLUrlEntryBase -{ -public: - LLUrlEntryChat(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); -}; - -/// -/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., -/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about -/// -class LLUrlEntryParcel : public LLUrlEntryBase -{ -public: - struct LLParcelData - { - LLUUID parcel_id; - std::string name; - std::string sim_name; - F32 global_x; - F32 global_y; - F32 global_z; - }; - - LLUrlEntryParcel(); - ~LLUrlEntryParcel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - - // Sends a parcel info request to sim. - void sendParcelInfoRequest(const LLUUID& parcel_id); - - // Calls observers of certain parcel id providing them with parcel label. - void onParcelInfoReceived(const std::string &id, const std::string &label); - - // Processes parcel label and triggers notifying observers. - static void processParcelInfo(const LLParcelData& parcel_data); - - // Next 4 setters are used to update agent and viewer connection information - // upon events like user login, viewer disconnect and user changing region host. - // These setters are made public to be accessible from newview and should not be - // used in other cases. - static void setAgentID(const LLUUID& id) { sAgentID = id; } - static void setSessionID(const LLUUID& id) { sSessionID = id; } - static void setRegionHost(const LLHost& host) { sRegionHost = host; } - static void setDisconnected(bool disconnected) { sDisconnected = disconnected; } - -private: - static LLUUID sAgentID; - static LLUUID sSessionID; - static LLHost sRegionHost; - static bool sDisconnected; - static std::set sParcelInfoObservers; -}; - -/// -/// LLUrlEntryPlace Describes a Second Life location Url, e.g., -/// secondlife://Ahern/50/50/50 -/// -class LLUrlEntryPlace : public LLUrlEntryBase -{ -public: - LLUrlEntryPlace(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; -}; - -/// -/// LLUrlEntryRegion Describes a Second Life location Url, e.g., -/// secondlife:///app/region/Ahern/128/128/0 -/// -class LLUrlEntryRegion : public LLUrlEntryBase -{ -public: - LLUrlEntryRegion(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; -}; - -/// -/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., -/// secondlife:///app/teleport/Ahern/50/50/50/ -/// -class LLUrlEntryTeleport : public LLUrlEntryBase -{ -public: - LLUrlEntryTeleport(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; -}; - -/// -/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts -/// with secondlife:// (used as a catch-all for cases not matched above) -/// -class LLUrlEntrySL : public LLUrlEntryBase -{ -public: - LLUrlEntrySL(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); -}; - -/// -/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts -/// with secondlife:// with the ability to specify a custom label. -/// -class LLUrlEntrySLLabel : public LLUrlEntryBase -{ -public: - LLUrlEntrySLLabel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; -}; - -/// -/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g., -/// secondlife:///app/worldmap/Ahern/50/50/50 -/// -class LLUrlEntryWorldMap : public LLUrlEntryBase -{ -public: - LLUrlEntryWorldMap(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; -}; - -/// -/// LLUrlEntryNoLink lets us turn of URL detection with ... tags -/// -class LLUrlEntryNoLink : public LLUrlEntryBase -{ -public: - LLUrlEntryNoLink(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ LLStyle::Params getStyle() const; -}; - -/// -/// LLUrlEntryIcon describes an icon with ... tags -/// -class LLUrlEntryIcon : public LLUrlEntryBase -{ -public: - LLUrlEntryIcon(); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getIcon(const std::string &url); -}; - -/// -/// LLUrlEntryEmail Describes a generic mailto: Urls -/// -class LLUrlEntryEmail : public LLUrlEntryBase -{ -public: - LLUrlEntryEmail(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; -}; - -/// -/// LLUrlEntryEmail Describes an IPv6 address -/// -class LLUrlEntryIPv6 : public LLUrlEntryBase -{ -public: - LLUrlEntryIPv6(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getQuery(const std::string &url) const; - - std::string mHostPath; -}; - -class LLKeyBindingToStringHandler; - -/// -/// LLUrlEntryKeybinding A way to access keybindings and show currently used one in text. -/// secondlife:///app/keybinding/control_name -class LLUrlEntryKeybinding: public LLUrlEntryBase -{ -public: - LLUrlEntryKeybinding(); - /*virtual*/ std::string getLabel(const std::string& url, const LLUrlLabelCallback& cb); - /*virtual*/ std::string getTooltip(const std::string& url) const; - void setHandler(LLKeyBindingToStringHandler* handler) {pHandler = handler;} -private: - std::string getControlName(const std::string& url) const; - std::string getMode(const std::string& url) const; - void initLocalization(); - void initLocalizationFromFile(const std::string& filename); - - struct LLLocalizationData - { - LLLocalizationData() {} - LLLocalizationData(const std::string& localization, const std::string& tooltip) - : mLocalization(localization) - , mTooltip(tooltip) - {} - std::string mLocalization; - std::string mTooltip; - }; - - std::map mLocalizations; - LLKeyBindingToStringHandler* pHandler; -}; - -#endif +/** + * @file llurlentry.h + * @author Martin Reddy + * @brief Describes the Url types that can be registered in LLUrlRegistry + * + * $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_LLURLENTRY_H +#define LL_LLURLENTRY_H + +#include "lluuid.h" +#include "lluicolor.h" +#include "llstyle.h" + +#include "llavatarname.h" +#include "llhost.h" // for resolving parcel name by parcel id + +#include +#include +#include +#include + +class LLAvatarName; + +typedef boost::signals2::signal LLUrlLabelSignal; +typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback; + +/// +/// LLUrlEntryBase is the base class of all Url types registered in the +/// LLUrlRegistry. Each derived classes provides a regular expression +/// to match the Url type (e.g., http://... or secondlife://...) along +/// with an optional icon to display next to instances of the Url in +/// a text display and a XUI file to use for any context menu popup. +/// Functions are also provided to compute an appropriate label and +/// tooltip/status bar text for the Url. +/// +/// Some derived classes of LLUrlEntryBase may wish to compute an +/// appropriate label for a Url by asking the server for information. +/// You must therefore provide a callback method, so that you can be +/// notified when an updated label has been received from the server. +/// This label should then be used to replace any previous label +/// that you received from getLabel() for the Url in question. +/// +class LLUrlEntryBase +{ +public: + LLUrlEntryBase(); + virtual ~LLUrlEntryBase(); + + /// Return the regex pattern that matches this Url + boost::regex getPattern() const { return mPattern; } + + /// Return the url from a string that matched the regex + virtual std::string getUrl(const std::string &string) const; + + /// Given a matched Url, return a label for the Url + virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; } + + /// Return port, query and fragment parts for the Url + virtual std::string getQuery(const std::string &url) const { return ""; } + + /// Return an icon that can be displayed next to Urls of this type + virtual std::string getIcon(const std::string &url); + + /// Return the style to render the displayed text + virtual LLStyle::Params getStyle() const; + + /// Given a matched Url, return a tooltip string for the hyperlink + virtual std::string getTooltip(const std::string &string) const { return mTooltip; } + + /// Return the name of a XUI file containing the context menu items + std::string getMenuName() const { return mMenuName; } + + /// Return the name of a SL location described by this Url, if any + virtual std::string getLocation(const std::string &url) const { return ""; } + + /// Should this link text be underlined only when mouse is hovered over it? + virtual bool underlineOnHoverOnly(const std::string &string) const { return false; } + + virtual bool isTrusted() const { return false; } + + virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } + + bool isLinkDisabled() const; + + bool isWikiLinkCorrect(const std::string &url) const; + + virtual bool isSLURLvalid(const std::string &url) const { return true; }; + +protected: + std::string getIDStringFromUrl(const std::string &url) const; + std::string escapeUrl(const std::string &url) const; + std::string unescapeUrl(const std::string &url) const; + std::string getLabelFromWikiLink(const std::string &url) const; + std::string getUrlFromWikiLink(const std::string &string) const; + void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb); + std::string urlToLabelWithGreyQuery(const std::string &url) const; + std::string urlToGreyQuery(const std::string &url) const; + virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon); + + typedef struct { + std::string url; + LLUrlLabelSignal *signal; + } LLUrlEntryObserver; + + boost::regex mPattern; + std::string mIcon; + std::string mMenuName; + std::string mTooltip; + std::multimap mObservers; +}; + +/// +/// LLUrlEntryHTTP Describes generic http: and https: Urls +/// +class LLUrlEntryHTTP : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTP(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getQuery(const std::string &url) const; + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getTooltip(const std::string &url) const; +}; + +/// +/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels +/// +class LLUrlEntryHTTPLabel : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTPLabel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getTooltip(const std::string &string) const; + /*virtual*/ std::string getUrl(const std::string &string) const; +}; + +class LLUrlEntryInvalidSLURL : public LLUrlEntryBase +{ +public: + LLUrlEntryInvalidSLURL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getTooltip(const std::string &url) const; + + bool isSLURLvalid(const std::string &url) const; +}; + +/// +/// LLUrlEntrySLURL Describes http://slurl.com/... Urls +/// +class LLUrlEntrySLURL : public LLUrlEntryBase +{ +public: + LLUrlEntrySLURL(); + /*virtual*/ bool isTrusted() const { return true; } + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls +/// +class LLUrlEntrySecondlifeURL : public LLUrlEntryBase +{ +public: + LLUrlEntrySecondlifeURL(); + /*virtual*/ bool isTrusted() const { return true; } + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getQuery(const std::string &url) const; + /*virtual*/ std::string getTooltip(const std::string &url) const; +}; + +/// +/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls +/// +class LLUrlEntrySimpleSecondlifeURL : public LLUrlEntrySecondlifeURL +{ +public: + LLUrlEntrySimpleSecondlifeURL(); +}; + +/// +/// LLUrlEntryAgent Describes a Second Life agent Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +class LLUrlEntryAgent : public LLUrlEntryBase +{ +public: + LLUrlEntryAgent(); + ~LLUrlEntryAgent() + { + for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mAvatarNameCacheConnections.clear(); + } + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getIcon(const std::string &url); + /*virtual*/ std::string getTooltip(const std::string &string) const; + /*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLUUID getID(const std::string &string) const; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; +protected: + /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); +private: + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); + + typedef std::map avatar_name_cache_connection_map_t; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; +}; + +/// +/// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +/// that displays various forms of user name +/// This is a base class for the various implementations of name display +class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable +{ +public: + LLUrlEntryAgentName(); + ~LLUrlEntryAgentName() + { + for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mAvatarNameCacheConnections.clear(); + } + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ LLStyle::Params getStyle() const; +protected: + // override this to pull out relevant name fields + virtual std::string getName(const LLAvatarName& avatar_name) = 0; +private: + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); + + typedef std::map avatar_name_cache_connection_map_t; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; +}; + + +/// +/// LLUrlEntryAgentCompleteName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +/// that displays the full display name + user name for an avatar +/// such as "James Linden (james.linden)" +class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentCompleteName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +class LLUrlEntryAgentLegacyName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentLegacyName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentDisplayName Describes a Second Life agent display name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +/// that displays the just the display name for an avatar +/// such as "James Linden" +class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentDisplayName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentUserName Describes a Second Life agent username Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +/// that displays the just the display name for an avatar +/// such as "james.linden" +class LLUrlEntryAgentUserName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentUserName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryExperienceProfile Describes a Second Life experience profile Url, e.g., +/// secondlife:///app/experience/0e346d8b-4433-4d66-a6b0-fd37083abc4c/profile +/// that displays the experience name +class LLUrlEntryExperienceProfile : public LLUrlEntryBase +{ +public: + LLUrlEntryExperienceProfile(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +private: + void onExperienceDetails(const LLSD& experience_details); +}; + + +/// +/// LLUrlEntryGroup Describes a Second Life group Url, e.g., +/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about +/// +class LLUrlEntryGroup : public LLUrlEntryBase +{ +public: + LLUrlEntryGroup(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLUUID getID(const std::string &string) const; +private: + void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); +}; + +/// +/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., +/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select +/// +class LLUrlEntryInventory : public LLUrlEntryBase +{ +public: + LLUrlEntryInventory(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +private: +}; + +/// +/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., +/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 +/// +class LLUrlEntryObjectIM : public LLUrlEntryBase +{ +public: + LLUrlEntryObjectIM(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +private: +}; + +// +// LLUrlEntryChat Describes a Second Life chat Url, e.g., +// secondlife:///app/chat/42/This%20Is%20a%20test +// +class LLUrlEntryChat : public LLUrlEntryBase +{ +public: + LLUrlEntryChat(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +}; + +/// +/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., +/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// +class LLUrlEntryParcel : public LLUrlEntryBase +{ +public: + struct LLParcelData + { + LLUUID parcel_id; + std::string name; + std::string sim_name; + F32 global_x; + F32 global_y; + F32 global_z; + }; + + LLUrlEntryParcel(); + ~LLUrlEntryParcel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + + // Sends a parcel info request to sim. + void sendParcelInfoRequest(const LLUUID& parcel_id); + + // Calls observers of certain parcel id providing them with parcel label. + void onParcelInfoReceived(const std::string &id, const std::string &label); + + // Processes parcel label and triggers notifying observers. + static void processParcelInfo(const LLParcelData& parcel_data); + + // Next 4 setters are used to update agent and viewer connection information + // upon events like user login, viewer disconnect and user changing region host. + // These setters are made public to be accessible from newview and should not be + // used in other cases. + static void setAgentID(const LLUUID& id) { sAgentID = id; } + static void setSessionID(const LLUUID& id) { sSessionID = id; } + static void setRegionHost(const LLHost& host) { sRegionHost = host; } + static void setDisconnected(bool disconnected) { sDisconnected = disconnected; } + +private: + static LLUUID sAgentID; + static LLUUID sSessionID; + static LLHost sRegionHost; + static bool sDisconnected; + static std::set sParcelInfoObservers; +}; + +/// +/// LLUrlEntryPlace Describes a Second Life location Url, e.g., +/// secondlife://Ahern/50/50/50 +/// +class LLUrlEntryPlace : public LLUrlEntryBase +{ +public: + LLUrlEntryPlace(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryRegion Describes a Second Life location Url, e.g., +/// secondlife:///app/region/Ahern/128/128/0 +/// +class LLUrlEntryRegion : public LLUrlEntryBase +{ +public: + LLUrlEntryRegion(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., +/// secondlife:///app/teleport/Ahern/50/50/50/ +/// +class LLUrlEntryTeleport : public LLUrlEntryBase +{ +public: + LLUrlEntryTeleport(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// (used as a catch-all for cases not matched above) +/// +class LLUrlEntrySL : public LLUrlEntryBase +{ +public: + LLUrlEntrySL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +}; + +/// +/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// with the ability to specify a custom label. +/// +class LLUrlEntrySLLabel : public LLUrlEntryBase +{ +public: + LLUrlEntrySLLabel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getTooltip(const std::string &string) const; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; +}; + +/// +/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g., +/// secondlife:///app/worldmap/Ahern/50/50/50 +/// +class LLUrlEntryWorldMap : public LLUrlEntryBase +{ +public: + LLUrlEntryWorldMap(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryNoLink lets us turn of URL detection with ... tags +/// +class LLUrlEntryNoLink : public LLUrlEntryBase +{ +public: + LLUrlEntryNoLink(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ LLStyle::Params getStyle() const; +}; + +/// +/// LLUrlEntryIcon describes an icon with ... tags +/// +class LLUrlEntryIcon : public LLUrlEntryBase +{ +public: + LLUrlEntryIcon(); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getIcon(const std::string &url); +}; + +/// +/// LLUrlEntryEmail Describes a generic mailto: Urls +/// +class LLUrlEntryEmail : public LLUrlEntryBase +{ +public: + LLUrlEntryEmail(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; +}; + +/// +/// LLUrlEntryEmail Describes an IPv6 address +/// +class LLUrlEntryIPv6 : public LLUrlEntryBase +{ +public: + LLUrlEntryIPv6(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getQuery(const std::string &url) const; + + std::string mHostPath; +}; + +class LLKeyBindingToStringHandler; + +/// +/// LLUrlEntryKeybinding A way to access keybindings and show currently used one in text. +/// secondlife:///app/keybinding/control_name +class LLUrlEntryKeybinding: public LLUrlEntryBase +{ +public: + LLUrlEntryKeybinding(); + /*virtual*/ std::string getLabel(const std::string& url, const LLUrlLabelCallback& cb); + /*virtual*/ std::string getTooltip(const std::string& url) const; + void setHandler(LLKeyBindingToStringHandler* handler) {pHandler = handler;} +private: + std::string getControlName(const std::string& url) const; + std::string getMode(const std::string& url) const; + void initLocalization(); + void initLocalizationFromFile(const std::string& filename); + + struct LLLocalizationData + { + LLLocalizationData() {} + LLLocalizationData(const std::string& localization, const std::string& tooltip) + : mLocalization(localization) + , mTooltip(tooltip) + {} + std::string mLocalization; + std::string mTooltip; + }; + + std::map mLocalizations; + LLKeyBindingToStringHandler* pHandler; +}; + +#endif diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index c46c2d9f6c..28283964e2 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1,2882 +1,2882 @@ -/** - * @file llview.cpp - * @author James Cook - * @brief Container for other views, anything that draws. - * - * $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 "linden_common.h" - -#define LLVIEW_CPP -#include "llview.h" - -#include -#include -#include - -#include "llrender.h" -#include "llevent.h" -#include "llfocusmgr.h" -#include "llrect.h" -#include "llstl.h" -#include "llui.h" -#include "lluictrl.h" -#include "llwindow.h" -#include "v3color.h" -#include "lluictrlfactory.h" -#include "lltooltip.h" -#include "llsdutil.h" -#include "llsdserialize.h" -#include "llviewereventrecorder.h" -#include "llkeyboard.h" -// for ui edit hack -#include "llbutton.h" -#include "lllineeditor.h" -#include "lltexteditor.h" -#include "lltextbox.h" - -static const S32 LINE_HEIGHT = 15; - -S32 LLView::sDepth = 0; -bool LLView::sDebugRects = false; -bool LLView::sDebugUnicode = false; -bool LLView::sDebugCamera = false; -bool LLView::sIsRectDirty = false; -LLRect LLView::sDirtyRect; -bool LLView::sDebugRectsShowNames = true; -bool LLView::sDebugKeys = false; -bool LLView::sDebugMouseHandling = false; -std::string LLView::sMouseHandlerMessage; -bool LLView::sForceReshape = false; -std::set LLView::sPreviewHighlightedElements; -bool LLView::sHighlightingDiffs = false; -LLView* LLView::sPreviewClickedElement = NULL; -bool LLView::sDrawPreviewHighlights = false; -S32 LLView::sLastLeftXML = S32_MIN; -S32 LLView::sLastBottomXML = S32_MIN; -std::vector LLViewDrawContext::sDrawContextStack; - -LLView::DrilldownFunc LLView::sDrilldown = - boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT); - -//#if LL_DEBUG -bool LLView::sIsDrawing = false; -//#endif - -// Compiler optimization, generate extern template -template class LLView* LLView::getChild( - const std::string& name, bool recurse) const; - -static LLDefaultChildRegistry::Register r("view"); - -void deleteView(LLView *aView) -{ - delete aView; -} - -namespace LLInitParam -{ - void TypeValues::declareValues() - { - declare("horizontal", LLView::HORIZONTAL); - declare("vertical", LLView::VERTICAL); - } -} - - -LLView::Follows::Follows() -: string(""), - flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP) -{} - -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", false), - 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 LLView::Params& p) -: mVisible(p.visible), - mInDraw(false), - mName(p.name), - mParentView(NULL), - mReshapeFlags(FOLLOWS_NONE), - mFromXUI(p.from_xui), - mIsFocusRoot(p.focus_root), - mLastVisible(false), - 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); - parseFollowsFlags(p); -} - -LLView::~LLView() -{ - dirtyRect(); - //LL_INFOS() << "Deleting view " << mName << ":" << (void*) this << LL_ENDL; - if (LLView::sIsDrawing) - { - LL_DEBUGS() << "Deleting view " << mName << " during UI draw() phase" << LL_ENDL; - } -// llassert(LLView::sIsDrawing == false); - -// llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators - - if( hasMouseCapture() ) - { - //LL_WARNS() << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << LL_ENDL; - gFocusMgr.removeMouseCaptureWithoutCallback( this ); - } - - deleteAllChildren(); - - if (mParentView != NULL) - { - mParentView->removeChild(this); - } - - if (mDefaultWidgets) - { - delete mDefaultWidgets; - mDefaultWidgets = NULL; - } -} - -// virtual -bool LLView::isCtrl() const -{ - return false; -} - -// virtual -bool LLView::isPanel() const -{ - return false; -} - -void LLView::setToolTip(const LLStringExplicit& msg) -{ - mToolTipMsg = msg; -} - -bool LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text) -{ - mToolTipMsg.setArg(key, text); - return true; -} - -void LLView::setToolTipArgs( const LLStringUtil::format_map_t& args ) -{ - mToolTipMsg.setArgList(args); -} - -// virtual -void LLView::setRect(const LLRect& rect) -{ - mRect = rect; - updateBoundingRect(); -} - -void LLView::setUseBoundingRect( bool use_bounding_rect ) -{ - if (mUseBoundingRect != use_bounding_rect) - { - mUseBoundingRect = use_bounding_rect; - updateBoundingRect(); - } -} - -bool LLView::getUseBoundingRect() const -{ - return mUseBoundingRect; -} - -// virtual -const std::string& LLView::getName() const -{ - static std::string no_name("(no name)"); - - return mName.empty() ? no_name : mName; -} - -void LLView::sendChildToFront(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, - // won't temporarily create an empty list - if (child != mChildList.front()) - { - mChildList.remove( child ); - mChildList.push_front(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, - // won't temporarily create an empty list - if (child != mChildList.back()) - { - mChildList.remove( child ); - mChildList.push_back(child); - } - } -} - -// virtual -bool LLView::addChild(LLView* child, S32 tab_group) -{ - if (!child) - { - return false; - } - - if (this == child) - { - LL_ERRS() << "Adding view " << child->getName() << " as child of itself" << LL_ENDL; - } - - // remove from current parent - if (child->mParentView) - { - child->mParentView->removeChild(child); - } - - // add to front of child list, as normal - mChildList.push_front(child); - - // add to tab order list - if (tab_group != 0) - { - mTabOrder.insert(tab_order_pair_t(child, tab_group)); - } - - child->mParentView = this; - if (getVisible() && child->getVisible()) - { - // if child isn't visible it won't affect bounding rect - // if current view is not visible it will be recalculated - // on visibility change - updateBoundingRect(); - } - mLastTabGroup = tab_group; - return true; -} - - -bool LLView::addChildInBack(LLView* child, S32 tab_group) -{ - if(addChild(child, tab_group)) - { - sendChildToBack(child); - return true; - } - - return false; -} - -// remove the specified child from the view, and set it's parent to NULL. -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); - mChildList.remove( child ); - child->mParentView = NULL; - child_tab_order_t::iterator found = mTabOrder.find(child); - if (found != mTabOrder.end()) - { - mTabOrder.erase(found); - } - } - else - { - LL_WARNS() << "\"" << child->getName() << "\" is not a child of " << getName() << LL_ENDL; - } - updateBoundingRect(); -} - -bool LLView::isInVisibleChain() const -{ - bool visible = true; - - const LLView* viewp = this; - while(viewp) - { - if (!viewp->getVisible()) - { - visible = false; - break; - } - viewp = viewp->getParent(); - } - - return visible; -} - -bool LLView::isInEnabledChain() const -{ - bool enabled = true; - - const LLView* viewp = this; - while(viewp) - { - if (!viewp->getEnabled()) - { - enabled = false; - break; - } - viewp = viewp->getParent(); - } - - return enabled; -} - -static void buildPathname(std::ostream& out, const LLView* view) -{ - if (! (view && view->getParent())) - { - return; // Don't include root in the path. - } - - buildPathname(out, view->getParent()); - - // Build pathname into ostream on the way back from recursion. - out << '/'; - - // substitute all '/' in name with appropriate code - std::string name = view->getName(); - std::size_t found = name.find('/'); - std::size_t start = 0; - while (found != std::string::npos) - { - std::size_t sub_len = found - start; - if (sub_len > 0) - { - out << name.substr(start, sub_len); - } - out << "%2F"; - start = found + 1; - found = name.find('/', start); - } - if (start < name.size()) - { - out << name.substr(start, name.size() - start); - } -} - -std::string LLView::getPathname() const -{ - std::ostringstream out; - buildPathname(out, this); - return out.str(); -} - -//static -std::string LLView::getPathname(const LLView* view) -{ - if (! view) - { - return "NULL"; - } - return view->getPathname(); -} - -// virtual -bool LLView::canFocusChildren() const -{ - return true; -} - -//virtual -void LLView::setEnabled(bool enabled) -{ - mEnabled = enabled; -} - -//virtual -bool LLView::isAvailable() const -{ - return isInEnabledChain() && isInVisibleChain(); -} - -//static -bool LLView::isAvailable(const LLView* view) -{ - return view && view->isAvailable(); -} - -//virtual -bool LLView::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - return false; -} - -//virtual -LLRect LLView::getSnapRect() const -{ - return mRect; -} - -//virtual -LLRect LLView::getRequiredRect() -{ - return mRect; -} - -bool LLView::focusNextRoot() -{ - LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); - return LLView::focusNext(result); -} - -bool LLView::focusPrevRoot() -{ - LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); - return LLView::focusPrev(result); -} - -// static -bool LLView::focusNext(LLView::child_list_t & result) -{ - LLView::child_list_reverse_iter_t focused = result.rend(); - for(LLView::child_list_reverse_iter_t iter = result.rbegin(); - iter != result.rend(); - ++iter) - { - if(gFocusMgr.childHasKeyboardFocus(*iter)) - { - focused = iter; - break; - } - } - LLView::child_list_reverse_iter_t next = focused; - next = (next == result.rend()) ? result.rbegin() : ++next; - while(next != focused) - { - // wrap around to beginning if necessary - if(next == result.rend()) - { - next = result.rbegin(); - } - if ((*next)->isCtrl() && ((LLUICtrl*)*next)->hasTabStop()) - { - LLUICtrl * ctrl = static_cast(*next); - ctrl->setFocus(true); - ctrl->onTabInto(); - gFocusMgr.triggerFocusFlash(); - return true; - } - ++next; - } - return false; -} - -// static -bool LLView::focusPrev(LLView::child_list_t & result) -{ - LLView::child_list_iter_t focused = result.end(); - for(LLView::child_list_iter_t iter = result.begin(); - iter != result.end(); - ++iter) - { - if(gFocusMgr.childHasKeyboardFocus(*iter)) - { - focused = iter; - break; - } - } - LLView::child_list_iter_t next = focused; - next = (next == result.end()) ? result.begin() : ++next; - while(next != focused) - { - // wrap around to beginning if necessary - if(next == result.end()) - { - next = result.begin(); - } - if((*next)->isCtrl()) - { - LLUICtrl * ctrl = static_cast(*next); - if (!ctrl->hasFocus()) - { - ctrl->setFocus(true); - ctrl->onTabInto(); - gFocusMgr.triggerFocusFlash(); - } - return true; - } - ++next; - } - return false; -} - -// delete all children. Override this function if you need to -// perform any extra clean up such as cached pointers to selected -// children, etc. -void LLView::deleteAllChildren() -{ - // clear out the control ordering - mTabOrder.clear(); - - while (!mChildList.empty()) - { - LLView* viewp = mChildList.front(); - viewp->mParentView = NULL; - delete viewp; - mChildList.pop_front(); - } - updateBoundingRect(); -} - -void LLView::setAllChildrenEnabled(bool b) -{ - for (LLView* viewp : mChildList) - { - viewp->setEnabled(b); - } -} - -// virtual -void LLView::setVisible(bool visible) -{ - if ( mVisible != visible ) - { - mVisible = visible; - - // notify children of visibility change if root, or part of visible hierarchy - if (!getParent() || getParent()->isInVisibleChain()) - { - // tell all children of this view that the visibility may have changed - dirtyRect(); - onVisibilityChange( visible ); - } - updateBoundingRect(); - } -} - -// virtual -void LLView::onVisibilityChange ( bool new_visibility ) -{ - bool old_visibility; - bool log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus(); - for (LLView* viewp : mChildList) - { - if (!viewp) - { - continue; - } - - // only views that are themselves visible will have their overall visibility affected by their ancestors - old_visibility=viewp->getVisible(); - - if(log_visibility_change) - { - if (old_visibility!=new_visibility) - { - LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget"); - } - } - - if (old_visibility) - { - viewp->onVisibilityChange ( new_visibility ); - } - - if(log_visibility_change) - { - // Consider changing returns to confirm success and know which widget grabbed it - // For now assume success and log at highest xui possible - // NOTE we log actual state - which may differ if it somehow failed to set visibility - LL_DEBUGS() << "LLView::handleVisibilityChange - now: " << getVisible() << " xui: " << viewp->getPathname() << " name: " << viewp->getName() << LL_ENDL; - - } - } -} - -// virtual -void LLView::onUpdateScrollToChild(const LLUICtrl * cntrl) -{ - LLView* parent_view = getParent(); - if (parent_view) - { - parent_view->onUpdateScrollToChild(cntrl); - } -} - -// virtual -void LLView::translate(S32 x, S32 y) -{ - mRect.translate(x, y); - updateBoundingRect(); -} - -// virtual -bool LLView::canSnapTo(const LLView* other_view) -{ - return other_view != this && other_view->getVisible(); -} - -// virtual -void LLView::setSnappedTo(const LLView* snap_view) -{ -} - -bool LLView::handleHover(S32 x, S32 y, MASK mask) -{ - return childrenHandleHover( x, y, mask ) != NULL; -} - -void LLView::onMouseEnter(S32 x, S32 y, MASK mask) -{ - //LL_INFOS() << "Mouse entered " << getName() << LL_ENDL; -} - -void LLView::onMouseLeave(S32 x, S32 y, MASK mask) -{ - //LL_INFOS() << "Mouse left " << getName() << LL_ENDL; -} - -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(); -} - -// This is NOT event recording related -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() ) - { - for (LLView* viewp : mChildList) - { - if ((viewp->*method)(c, mask, true)) - { - if (LLView::sDebugKeys) - { - LL_INFOS() << desc << " handled by " << viewp->getName() << LL_ENDL; - } - 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) -{ - for (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 ))) - { - LL_DEBUGS() << "LLView::childrenHandleMouseEvent calling updatemouseeventinfo - local_x|global x "<< local_x << " " << x << "local/global y " << local_y << " " << y << LL_ENDL; - LL_DEBUGS() << "LLView::childrenHandleMouseEvent getPathname for viewp result: " << viewp->getPathname() << "for this view: " << getPathname() << LL_ENDL; - - LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); - - // This is NOT event recording related - viewp->logMouseEvent(); - - return viewp; - } - } - return NULL; -} - -LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask) -{ - for (LLView* viewp : mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - // Differs from childrenHandleMouseEvent() in that we want to offer - // tooltips even for disabled widgets. - if(!viewp->visibleAndContains(local_x, local_y)) - { - continue; - } - - if (viewp->handleToolTip(local_x, local_y, mask) - || viewp->blockMouseEvent(local_x, local_y)) - { - // This is NOT event recording related - 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; - - for (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) -{ - for (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::getInstance()->mWindow->setCursor(viewp->getHoverCursor()); - - if (viewp->handleHover(local_x, local_y, mask) - || viewp->blockMouseEvent(local_x, local_y)) - { - // This is NOT event recording related - viewp->logMouseEvent(); - return viewp; - } - } - return NULL; -} - -LLView* LLView::childFromPoint(S32 x, S32 y, bool recur) -{ - if (!getVisible()) - return NULL; - - for (LLView* viewp : mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->visibleAndContains(local_x, local_y)) - { - continue; - } - // Here we've found the first (frontmost) visible child at this level - // containing the specified point. Is the caller asking us to drill - // down and return the innermost leaf child at this point, or just the - // top-level child? - if (recur) - { - LLView* leaf(viewp->childFromPoint(local_x, local_y, recur)); - // Maybe viewp is already a leaf LLView, or maybe it has children - // but this particular (x, y) point falls between them. If the - // recursive call returns non-NULL, great, use that; else just use - // viewp. - return leaf? leaf : viewp; - } - return viewp; - - } - return 0; -} - -F32 LLView::getTooltipTimeout() -{ - static LLCachedControl tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f); - static LLCachedControl tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f); - // allow "scrubbing" over ui by showing next tooltip immediately - // if previous one was still visible - return (F32)(LLToolTipMgr::instance().toolTipVisible() - ? tooltip_fast_delay - : tooltip_delay); -} - -// virtual -const std::string LLView::getToolTip() const -{ - if (sDebugUnicode) - { - std::string text = getText(); - if (!text.empty()) - { - const std::string& name = getName(); - std::string tooltip = llformat("Name: \"%s\"", name.c_str()); - - if (const LLFontGL* font = getFont()) - { - tooltip += llformat("\nFont: %s (%s)", - font->getFontDesc().getName().c_str(), - font->getFontDesc().getSize().c_str() - ); - } - - tooltip += "\n\n" + utf8str_showBytesUTF8(text); - - return tooltip; - } - } - - return mToolTipMsg.getString(); -} - -bool LLView::handleToolTip(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // parents provide tooltips first, which are optionally - // overridden by children, in case child is mouse_opaque - std::string tooltip = getToolTip(); - if (!tooltip.empty()) - { - static LLCachedControl allow_ui_tooltips(*LLUI::getInstance()->mSettingGroups["config"], "BasicUITooltips", true); - - // Even if we don't show tooltips, consume the event, nothing below should show tooltip - if (allow_ui_tooltips) - { - LLToolTipMgr::instance().show(LLToolTip::Params() - .message(tooltip) - .sticky_rect(calcScreenRect()) - .delay_time(getTooltipTimeout())); - } - handled = true; - } - - // child tooltips will override our own - LLView* child_handler = childrenHandleToolTip(x, y, mask); - if (child_handler) - { - handled = true; - } - - return handled; -} - -bool LLView::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - bool handled = false; - - if (getVisible() && getEnabled()) - { - if( called_from_parent ) - { - // Downward traversal - handled = childrenHandleKey( key, mask ) != NULL; - } - - if (!handled) - { - // For event logging we don't care which widget handles it - // So we capture the key at the end of this function once we know if it was handled - handled = handleKeyHere( key, mask ); - if (handled) - { - LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL; - } - } - } - - if( !handled && !called_from_parent && mParentView) - { - // Upward traversal - handled = mParentView->handleKey( key, mask, false ); - } - return handled; -} - -bool LLView::handleKeyUp(KEY key, MASK mask, bool called_from_parent) -{ - bool handled = false; - - if (getVisible() && getEnabled()) - { - if (called_from_parent) - { - // Downward traversal - handled = childrenHandleKeyUp(key, mask) != NULL; - } - - if (!handled) - { - // For event logging we don't care which widget handles it - // So we capture the key at the end of this function once we know if it was handled - handled = handleKeyUpHere(key, mask); - if (handled) - { - LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL; - } - } - } - - if (!handled && !called_from_parent && mParentView) - { - // Upward traversal - handled = mParentView->handleKeyUp(key, mask, false); - } - return handled; -} - -// Called from handleKey() -// Handles key in this object. Checking parents and children happens in handleKey() -bool LLView::handleKeyHere(KEY key, MASK mask) -{ - return false; -} - -// Called from handleKey() -// Handles key in this object. Checking parents and children happens in handleKey() -bool LLView::handleKeyUpHere(KEY key, MASK mask) -{ - return false; -} - -bool LLView::handleUnicodeChar(llwchar uni_char, bool called_from_parent) -{ - bool handled = false; - - if (getVisible() && getEnabled()) - { - if( called_from_parent ) - { - // Downward traversal - handled = childrenHandleUnicodeChar( uni_char ) != NULL; - } - - if (!handled) - { - handled = handleUnicodeCharHere(uni_char); - if (handled && LLView::sDebugKeys) - { - LL_INFOS() << "Unicode key " << wchar_utf8_preview(uni_char) << " is handled by " << getName() << LL_ENDL; - } - } - } - - if (!handled && !called_from_parent && mParentView) - { - // Upward traversal - handled = mParentView->handleUnicodeChar(uni_char, false); - } - - if (handled) - { - LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char); - } - - return handled; -} - - -bool LLView::handleUnicodeCharHere(llwchar uni_char ) -{ - return false; -} - - -bool LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, - EDragAndDropType cargo_type, void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; -} - -void LLView::onMouseCaptureLost() -{ -} - -bool LLView::hasMouseCapture() -{ - return gFocusMgr.getMouseCapture() == this; -} - -bool LLView::handleMouseUp(S32 x, S32 y, MASK mask) -{ - LLView* r = childrenHandleMouseUp( x, y, mask ); - - return (r!=NULL); -} - -bool LLView::handleMouseDown(S32 x, S32 y, MASK mask) -{ - LLView* r= childrenHandleMouseDown(x, y, mask ); - - return (r!=NULL); -} - -bool LLView::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - return childrenHandleDoubleClick( x, y, mask ) != NULL; -} - -bool LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - return childrenHandleScrollWheel( x, y, clicks ) != NULL; -} - -bool LLView::handleScrollHWheel(S32 x, S32 y, S32 clicks) -{ - return childrenHandleScrollHWheel( x, y, clicks ) != NULL; -} - -bool LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - return childrenHandleRightMouseDown( x, y, mask ) != NULL; -} - -bool LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) -{ - return childrenHandleRightMouseUp( x, y, mask ) != NULL; -} - -bool LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - return childrenHandleMiddleMouseDown( x, y, mask ) != NULL; -} - -bool LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) -{ - return childrenHandleMiddleMouseUp( x, y, mask ) != NULL; -} - -LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks) -{ - return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false); -} - -LLView* LLView::childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks) -{ - return childrenHandleMouseEvent(&LLView::handleScrollHWheel, x, y, clicks, false); -} - -// Called during downward traversal -LLView* LLView::childrenHandleKey(KEY key, MASK mask) -{ - return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask); -} - -// Called during downward traversal -LLView* LLView::childrenHandleKeyUp(KEY key, MASK mask) -{ - return childrenHandleCharEvent("Key Up", &LLView::handleKeyUp, key, mask); -} - -// Called during downward traversal -LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char) -{ - return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask, - uni_char, MASK_NONE); -} - -LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask) -{ - return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask); -} - -LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask) -{ - return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask); -} - -LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask); -} - -LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask) -{ - return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask); -} - -LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask) -{ - return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask); -} - -LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask) -{ - return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask); -} - -LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) -{ - return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask); -} - -void LLView::draw() -{ - drawChildren(); -} - -void LLView::drawChildren() -{ - if (!mChildList.empty()) - { - LLView* rootp = LLUI::getInstance()->getRootView(); - ++sDepth; - - for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter) - { - child_list_reverse_iter_t child = child_iter++; - LLView *viewp = *child; - - if (viewp == NULL) - { - continue; - } - - if (viewp->getVisible() && viewp->getRect().isValid()) - { - LLRect screen_rect = viewp->calcScreenRect(); - if ( rootp->getLocalRect().overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect)) - { - LLUI::pushMatrix(); - { - LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom); - // 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()) - { - LL_WARNS() << "Bogus rectangle for " << getName() << " with " << mRect << LL_ENDL; - } - } - } - LLUI::popMatrix(); - } - } - - } - --sDepth; - } -} - -void LLView::dirtyRect() -{ - LLView* child = getParent(); - LLView* parent = child ? child->getParent() : NULL; - LLView* cur = this; - while (child && parent && parent->getParent()) - { //find third to top-most view - cur = child; - child = parent; - parent = parent->getParent(); - } - - if (!sIsRectDirty) - { - sDirtyRect = cur->calcScreenRect(); - sIsRectDirty = true; - } - else - { - sDirtyRect.unionWith(cur->calcScreenRect()); - } -} - -//Draw a box for debugging. -void LLView::drawDebugRect() -{ - std::set::iterator preview_iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); // figure out if it's a previewed element - - LLUI::pushMatrix(); - { - // drawing solids requires texturing be disabled - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - if (getUseBoundingRect()) - { - LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom); - } - - LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect; - - // draw red rectangle for the border - LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f); - if(preview_iter != sPreviewHighlightedElements.end()) - { - if(LLView::sPreviewClickedElement && this == sPreviewClickedElement) - { - border_color = LLColor4::red; - } - else - { - static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor"); - border_color = scroll_highlighted_color; - } - } - else - { - border_color.mV[sDepth%3] = 1.f; - } - - gGL.color4fv( border_color.mV ); - - gGL.begin(LLRender::LINES); - gGL.vertex2i(0, debug_rect.getHeight() - 1); - gGL.vertex2i(0, 0); - - gGL.vertex2i(0, 0); - gGL.vertex2i(debug_rect.getWidth() - 1, 0); - - gGL.vertex2i(debug_rect.getWidth() - 1, 0); - gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); - - gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); - gGL.vertex2i(0, debug_rect.getHeight() - 1); - gGL.end(); - - // Draw the name if it's not a leaf node or not in editing or preview mode - if (mChildList.size() - && preview_iter == sPreviewHighlightedElements.end() - && sDebugRectsShowNames) - { - S32 x, y; - gGL.color4fv( border_color.mV ); - - x = debug_rect.getWidth() / 2; - - S32 rect_height = debug_rect.getHeight(); - S32 lines = rect_height / LINE_HEIGHT + 1; - - S32 depth = 0; - LLView * viewp = this; - while (NULL != viewp) - { - viewp = viewp->getParent(); - depth++; - } - - y = rect_height - LINE_HEIGHT * (depth % lines + 1); - - std::string debug_text = llformat("%s (%d x %d)", getName().c_str(), - debug_rect.getWidth(), debug_rect.getHeight()); - LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, - LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW); - } - } - LLUI::popMatrix(); -} - -void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, bool force_draw) -{ - if (childp && childp->getParent() == this) - { - ++sDepth; - - if ((childp->getVisible() && childp->getRect().isValid()) - || force_draw) - { - gGL.matrixMode(LLRender::MM_MODELVIEW); - LLUI::pushMatrix(); - { - LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset); - childp->draw(); - } - LLUI::popMatrix(); - } - - --sDepth; - } -} - - -void LLView::reshape(S32 width, S32 height, bool called_from_parent) -{ - // compute how much things changed and apply reshape logic to children - S32 delta_width = width - getRect().getWidth(); - S32 delta_height = height - getRect().getHeight(); - - if (delta_width || delta_height || sForceReshape) - { - // adjust our rectangle - mRect.mRight = getRect().mLeft + width; - mRect.mTop = getRect().mBottom + height; - - // move child views according to reshape flags - for (LLView* viewp : mChildList) - { - if (viewp != NULL) - { - LLRect child_rect( viewp->mRect ); - - if (viewp->followsRight() && viewp->followsLeft()) - { - child_rect.mRight += delta_width; - } - else if (viewp->followsRight()) - { - child_rect.mLeft += delta_width; - child_rect.mRight += delta_width; - } - else if (viewp->followsLeft()) - { - // left is 0, don't need to adjust coords - } - else - { - // BUG what to do when we don't follow anyone? - // for now, same as followsLeft - } - - if (viewp->followsTop() && viewp->followsBottom()) - { - child_rect.mTop += delta_height; - } - else if (viewp->followsTop()) - { - child_rect.mTop += delta_height; - child_rect.mBottom += delta_height; - } - else if (viewp->followsBottom()) - { - // bottom is 0, so don't need to adjust coords - } - else - { - // BUG what to do when we don't follow? - // for now, same as bottom - } - - S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft; - S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom; - viewp->translate( delta_x, delta_y ); - if (child_rect.getWidth() != viewp->getRect().getWidth() - || child_rect.getHeight() != viewp->getRect().getHeight() - || sForceReshape) - { - viewp->reshape(child_rect.getWidth(), child_rect.getHeight()); - } - } - } - } - - if (!called_from_parent) - { - if (mParentView) - { - mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), false); - } - } - - updateBoundingRect(); -} - -LLRect LLView::calcBoundingRect() -{ - LLRect local_bounding_rect = LLRect::null; - - for (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.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 - { - mBoundingRect = mRect; - } - - // give parent view a chance to resize, in case we just moved, for example - if (getParent() && getParent()->getUseBoundingRect()) - { - getParent()->updateBoundingRect(); - } - - if (mBoundingRect != cur_rect) - { - dirtyRect(); - } - -} - -LLRect LLView::calcScreenRect() const -{ - 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(); - local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom); - - return local_bounding_rect; -} - - -LLRect LLView::getLocalRect() const -{ - LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0); - return local_rect; -} - -LLRect LLView::getLocalSnapRect() const -{ - LLRect local_snap_rect = getSnapRect(); - local_snap_rect.translate(-getRect().mLeft, -getRect().mBottom); - return local_snap_rect; -} - -bool LLView::hasAncestor(const LLView* parentp) const -{ - if (!parentp) - { - return false; - } - - LLView* viewp = getParent(); - while(viewp) - { - if (viewp == parentp) - { - return true; - } - viewp = viewp->getParent(); - } - - return false; -} - -//----------------------------------------------------------------------------- - -bool LLView::childHasKeyboardFocus( const std::string& childname ) const -{ - LLView *focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); - - while (focus != NULL) - { - if (focus->getName() == childname) - { - return true; - } - - focus = focus->getParent(); - } - - return false; -} - -//----------------------------------------------------------------------------- - -bool LLView::hasChild(const std::string& childname, bool recurse) const -{ - return findChildView(childname, recurse) != NULL; -} - -//----------------------------------------------------------------------------- -// getChildView() -//----------------------------------------------------------------------------- -LLView* LLView::getChildView(const std::string& name, bool recurse) const -{ - return getChild(name, recurse); -} - -LLView* LLView::findChildView(const std::string& name, bool recurse) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - - // Look for direct children *first* - for (LLView* childp : mChildList) - { - llassert(childp); - if (childp->getName() == name) - { - return childp; - } - } - if (recurse) - { - // Look inside each child as well. - for (LLView* childp : mChildList) - { - llassert(childp); - LLView* viewp = childp->findChildView(name, recurse); - if ( viewp ) - { - return viewp; - } - } - } - return NULL; -} - -bool LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const -{ - 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 (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT) - ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) - : mRect.localPointInRect( x, y ); -} - -bool LLView::blockMouseEvent(S32 x, S32 y) const -{ - return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT); -} - -// virtual -void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const -{ - *local_x = screen_x - getRect().mLeft; - *local_y = screen_y - getRect().mBottom; - - const LLView* cur = this; - while( cur->mParentView ) - { - cur = cur->mParentView; - *local_x -= cur->getRect().mLeft; - *local_y -= cur->getRect().mBottom; - } -} - -void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const -{ - *screen_x = local_x; - *screen_y = local_y; - - const LLView* cur = this; - do - { - LLRect cur_rect = cur->getRect(); - *screen_x += cur_rect.mLeft; - *screen_y += cur_rect.mBottom; - cur = cur->mParentView; - } - while( cur ); -} - -void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const -{ - *local = screen; - local->translate( -getRect().mLeft, -getRect().mBottom ); - - const LLView* cur = this; - while( cur->mParentView ) - { - cur = cur->mParentView; - local->translate( -cur->getRect().mLeft, -cur->getRect().mBottom ); - } -} - -void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const -{ - *screen = local; - screen->translate( getRect().mLeft, getRect().mBottom ); - - const LLView* cur = this; - while( cur->mParentView ) - { - cur = cur->mParentView; - screen->translate( cur->getRect().mLeft, cur->getRect().mBottom ); - } -} - -LLView* LLView::getRootView() -{ - LLView* view = this; - while( view->mParentView ) - { - view = view->mParentView; - } - return view; -} - -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, S32 min_overlap_pixels) -{ - LLCoordGL delta; - - const S32 KEEP_ONSCREEN_PIXELS_WIDTH = llmin(min_overlap_pixels, input.getWidth()); - const S32 KEEP_ONSCREEN_PIXELS_HEIGHT = llmin(min_overlap_pixels, input.getHeight()); - - if (KEEP_ONSCREEN_PIXELS_WIDTH <= constraint.getWidth() && - KEEP_ONSCREEN_PIXELS_HEIGHT <= constraint.getHeight()) - { - if (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft) - { - delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH); - } - else if (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight) - { - delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH); - } - - if (input.mTop > constraint.mTop) - { - delta.mY = constraint.mTop - input.mTop; - } - else if (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom) - { - delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT); - } - } - - 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, S32 min_overlap_pixels) -{ - return translateRectIntoRect(getRect(), constraint, min_overlap_pixels); -} - -bool LLView::translateRectIntoRect(const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels) -{ - LLCoordGL translation = getNeededTranslation(rect, constraint, min_overlap_pixels); - - if (translation.mX != 0 || translation.mY != 0) - { - 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, S32 min_overlap_pixels) -{ - LLCoordGL translation = getNeededTranslation(getRect(), inside, min_overlap_pixels); - - 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; - S32 bottom = bounds.mBottom + (bounds.getHeight() - getRect().getHeight()) / 2; - - translate( left - getRect().mLeft, bottom - getRect().mBottom ); -} - -bool LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const -{ - const LLView* cur_view = this; - const LLView* root_view = NULL; - - while (cur_view) - { - if (cur_view == other_view) - { - *other_x = x; - *other_y = y; - return true; - } - - x += cur_view->getRect().mLeft; - y += cur_view->getRect().mBottom; - - cur_view = cur_view->getParent(); - root_view = cur_view; - } - - // assuming common root between two views, chase other_view's parents up to root - cur_view = other_view; - while (cur_view) - { - x -= cur_view->getRect().mLeft; - y -= cur_view->getRect().mBottom; - - cur_view = cur_view->getParent(); - - if (cur_view == root_view) - { - *other_x = x; - *other_y = y; - return true; - } - } - - *other_x = x; - *other_y = y; - return false; -} - -bool LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const -{ - LLRect cur_rect = local; - const LLView* cur_view = this; - const LLView* root_view = NULL; - - while (cur_view) - { - if (cur_view == other_view) - { - *other = cur_rect; - return true; - } - - cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom); - - cur_view = cur_view->getParent(); - root_view = cur_view; - } - - // assuming common root between two views, chase other_view's parents up to root - cur_view = other_view; - while (cur_view) - { - cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom); - - cur_view = cur_view->getParent(); - - if (cur_view == root_view) - { - *other = cur_rect; - return true; - } - } - - *other = cur_rect; - return false; -} - - -class CompareByTabOrder -{ -public: - CompareByTabOrder(const LLView::child_tab_order_t& order, S32 default_tab_group = 0) - : mTabOrder(order), - mDefaultTabGroup(default_tab_group) - {} - virtual ~CompareByTabOrder() {} - - // This method compares two LLViews by the tab order specified in the comparator object. The - // code for this is a little convoluted because each argument can have four states: - // 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null - bool operator() (const LLView* const a, const LLView* const b) const - { - S32 a_group = 0, b_group = 0; - if(!a) return false; - if(!b) return true; - - LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a), b_found = mTabOrder.find(b); - if(a_found != mTabOrder.end()) - { - a_group = a_found->second; - } - if(b_found != mTabOrder.end()) - { - b_group = b_found->second; - } - - if(a_group < mDefaultTabGroup && b_group >= mDefaultTabGroup) return true; - if(b_group < mDefaultTabGroup && a_group >= mDefaultTabGroup) return false; - return a_group > b_group; // sort correctly if they're both on the same side of the default tab groupreturn a > b; - } -private: - // ok to store a reference, as this should only be allocated on stack during view query operations - const LLView::child_tab_order_t& mTabOrder; - const S32 mDefaultTabGroup; -}; - -class SortByTabOrder : public LLQuerySorter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(SortByTabOrder); - /*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const override - { - children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup())); - } -}; - -// static -const LLViewQuery & LLView::getTabOrderQuery() -{ - static LLViewQuery query; - if(query.getPreFilters().size() == 0) { - query.addPreFilter(LLVisibleFilter::getInstance()); - query.addPreFilter(LLEnabledFilter::getInstance()); - query.addPreFilter(LLTabStopFilter::getInstance()); - query.addPostFilter(LLLeavesFilter::getInstance()); - query.setSorter(SortByTabOrder::getInstance()); - } - return query; -} - -// This class is only used internally by getFocusRootsQuery below. -class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override - { - return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot()); - } -}; - -// static -const LLViewQuery & LLView::getFocusRootsQuery() -{ - static LLViewQuery query; - if(query.getPreFilters().size() == 0) { - query.addPreFilter(LLVisibleFilter::getInstance()); - query.addPreFilter(LLEnabledFilter::getInstance()); - query.addPreFilter(LLFocusRootsFilter::getInstance()); - query.addPostFilter(LLRootsFilter::getInstance()); - } - return query; -} - - -void LLView::setShape(const LLRect& new_rect, bool by_user) -{ - if (new_rect != getRect()) - { - handleReshape(new_rect, by_user); - } -} - -void LLView::handleReshape(const LLRect& new_rect, bool by_user) -{ - reshape(new_rect.getWidth(), new_rect.getHeight()); - translate(new_rect.mLeft - getRect().mLeft, new_rect.mBottom - getRect().mBottom); -} - -LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, - LLView::ESnapType snap_type, S32 threshold, S32 padding) -{ - new_rect = mRect; - LLView* snap_view = NULL; - - if (!mParentView) - { - return NULL; - } - - S32 delta_x = 0; - S32 delta_y = 0; - if (mouse_dir.mX >= 0) - { - S32 new_right = mRect.mRight; - LLView* view = findSnapEdge(new_right, mouse_dir, SNAP_RIGHT, snap_type, threshold, padding); - delta_x = new_right - mRect.mRight; - snap_view = view ? view : snap_view; - } - - if (mouse_dir.mX <= 0) - { - S32 new_left = mRect.mLeft; - LLView* view = findSnapEdge(new_left, mouse_dir, SNAP_LEFT, snap_type, threshold, padding); - delta_x = new_left - mRect.mLeft; - snap_view = view ? view : snap_view; - } - - if (mouse_dir.mY >= 0) - { - S32 new_top = mRect.mTop; - LLView* view = findSnapEdge(new_top, mouse_dir, SNAP_TOP, snap_type, threshold, padding); - delta_y = new_top - mRect.mTop; - snap_view = view ? view : snap_view; - } - - if (mouse_dir.mY <= 0) - { - S32 new_bottom = mRect.mBottom; - LLView* view = findSnapEdge(new_bottom, mouse_dir, SNAP_BOTTOM, snap_type, threshold, padding); - delta_y = new_bottom - mRect.mBottom; - snap_view = view ? view : snap_view; - } - - new_rect.translate(delta_x, delta_y); - return snap_view; -} - -LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) -{ - LLRect snap_rect = getSnapRect(); - S32 snap_pos = 0; - switch(snap_edge) - { - case SNAP_LEFT: - snap_pos = snap_rect.mLeft; - break; - case SNAP_RIGHT: - snap_pos = snap_rect.mRight; - break; - case SNAP_TOP: - snap_pos = snap_rect.mTop; - break; - case SNAP_BOTTOM: - snap_pos = snap_rect.mBottom; - break; - } - - if (!mParentView) - { - new_edge_val = snap_pos; - return NULL; - } - - LLView* snap_view = NULL; - - // If the view is near the edge of its parent, snap it to - // the edge. - LLRect test_rect = snap_rect; - test_rect.stretch(padding); - - S32 x_threshold = threshold; - S32 y_threshold = threshold; - - LLRect parent_local_snap_rect = mParentView->getLocalSnapRect(); - - if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS) - { - switch(snap_edge) - { - case SNAP_RIGHT: - if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= x_threshold - && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0) - { - snap_pos = parent_local_snap_rect.mRight - padding; - snap_view = mParentView; - x_threshold = llabs(parent_local_snap_rect.mRight - test_rect.mRight); - } - break; - case SNAP_LEFT: - if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= x_threshold - && test_rect.mLeft * mouse_dir.mX <= 0) - { - snap_pos = parent_local_snap_rect.mLeft + padding; - snap_view = mParentView; - x_threshold = llabs(test_rect.mLeft - parent_local_snap_rect.mLeft); - } - break; - case SNAP_BOTTOM: - if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= y_threshold - && test_rect.mBottom * mouse_dir.mY <= 0) - { - snap_pos = parent_local_snap_rect.mBottom + padding; - snap_view = mParentView; - y_threshold = llabs(test_rect.mBottom - parent_local_snap_rect.mBottom); - } - break; - case SNAP_TOP: - if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= y_threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0) - { - snap_pos = parent_local_snap_rect.mTop - padding; - snap_view = mParentView; - y_threshold = llabs(parent_local_snap_rect.mTop - test_rect.mTop); - } - break; - default: - LL_ERRS() << "Invalid snap edge" << LL_ENDL; - } - } - - if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS) - { - for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin(); - child_it != mParentView->getChildList()->end(); ++child_it) - { - LLView* siblingp = *child_it; - - if (!canSnapTo(siblingp)) continue; - - LLRect sibling_rect = siblingp->getSnapRect(); - - switch(snap_edge) - { - case SNAP_RIGHT: - if (llabs(test_rect.mRight - sibling_rect.mLeft) <= x_threshold - && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0) - { - snap_pos = sibling_rect.mLeft - padding; - snap_view = siblingp; - x_threshold = llabs(test_rect.mRight - sibling_rect.mLeft); - } - // if snapped with sibling along other axis, check for shared edge - else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold - || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= x_threshold) - { - if (llabs(test_rect.mRight - sibling_rect.mRight) <= x_threshold - && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0) - { - snap_pos = sibling_rect.mRight; - snap_view = siblingp; - x_threshold = llabs(test_rect.mRight - sibling_rect.mRight); - } - } - break; - case SNAP_LEFT: - if (llabs(test_rect.mLeft - sibling_rect.mRight) <= x_threshold - && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0) - { - snap_pos = sibling_rect.mRight + padding; - snap_view = siblingp; - x_threshold = llabs(test_rect.mLeft - sibling_rect.mRight); - } - // if snapped with sibling along other axis, check for shared edge - else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold - || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= y_threshold) - { - if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= x_threshold - && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0) - { - snap_pos = sibling_rect.mLeft; - snap_view = siblingp; - x_threshold = llabs(test_rect.mLeft - sibling_rect.mLeft); - } - } - break; - case SNAP_BOTTOM: - if (llabs(test_rect.mBottom - sibling_rect.mTop) <= y_threshold - && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0) - { - snap_pos = sibling_rect.mTop + padding; - snap_view = siblingp; - y_threshold = llabs(test_rect.mBottom - sibling_rect.mTop); - } - // if snapped with sibling along other axis, check for shared edge - else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold - || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold) - { - if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= y_threshold - && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0) - { - snap_pos = sibling_rect.mBottom; - snap_view = siblingp; - y_threshold = llabs(test_rect.mBottom - sibling_rect.mBottom); - } - } - break; - case SNAP_TOP: - if (llabs(test_rect.mTop - sibling_rect.mBottom) <= y_threshold - && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0) - { - snap_pos = sibling_rect.mBottom - padding; - snap_view = siblingp; - y_threshold = llabs(test_rect.mTop - sibling_rect.mBottom); - } - // if snapped with sibling along other axis, check for shared edge - else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold - || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold) - { - if (llabs(test_rect.mTop - sibling_rect.mTop) <= y_threshold - && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0) - { - snap_pos = sibling_rect.mTop; - snap_view = siblingp; - y_threshold = llabs(test_rect.mTop - sibling_rect.mTop); - } - } - break; - default: - LL_ERRS() << "Invalid snap edge" << LL_ENDL; - } - } - } - - new_edge_val = snap_pos; - return snap_view; -} - -//----------------------------------------------------------------------------- -// Listener dispatch functions -//----------------------------------------------------------------------------- - - -LLControlVariable *LLView::findControl(const std::string& name) -{ - // parse the name to locate which group it belongs to - std::size_t key_pos= name.find("."); - if(key_pos!= std::string::npos ) - { - std::string control_group_key = name.substr(0, key_pos); - LLControlVariable* control; - // check if it's in the control group that name indicated - if(LLUI::getInstance()->mSettingGroups[control_group_key]) - { - control = LLUI::getInstance()->mSettingGroups[control_group_key]->getControl(name); - if (control) - { - return control; - } - } - } - - LLControlGroup& control_group = LLUI::getInstance()->getControlControlGroup(name); - return control_group.getControl(name); -} - -void LLView::initFromParams(const LLView::Params& params) -{ - LLRect required_rect = getRequiredRect(); - - S32 width = llmax(getRect().getWidth(), required_rect.getWidth()); - S32 height = llmax(getRect().getHeight(), required_rect.getHeight()); - - reshape(width, height); - - // call virtual methods with most recent data - // use getters because these values might not come through parameter block - setEnabled(getEnabled()); - setVisible(getVisible()); - - if (!params.name().empty()) - { - setName(params.name()); - } - - mLayout = params.layout(); -} - -void LLView::parseFollowsFlags(const LLView::Params& params) -{ - // preserve follows flags set by code if user did not override - if (!params.follows.isProvided()) - { - return; - } - - // interpret either string or bitfield version of follows - if (params.follows.string.isChosen()) - { - setFollows(FOLLOWS_NONE); - - std::string follows = params.follows.string; - - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|"); - tokenizer tokens(follows, sep); - tokenizer::iterator token_iter = tokens.begin(); - - while(token_iter != tokens.end()) - { - const std::string& token_str = *token_iter; - if (token_str == "left") - { - setFollowsLeft(); - } - else if (token_str == "right") - { - setFollowsRight(); - } - else if (token_str == "top") - { - setFollowsTop(); - } - else if (token_str == "bottom") - { - setFollowsBottom(); - } - else if (token_str == "all") - { - setFollowsAll(); - } - ++token_iter; - } - } - else if (params.follows.flags.isChosen()) - { - setFollows(params.follows.flags); - } -} - - -// static -//LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node) -//{ -// LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT; -// -// if (node->hasAttribute("halign")) -// { -// std::string horizontal_align_name; -// node->getAttributeString("halign", horizontal_align_name); -// gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name); -// } -// return gl_hfont_align; -//} - -// Return the rectangle of the last-constructed child, -// if present and a first-class widget (eg, not a close box or drag handle) -// Returns true if found -static bool get_last_child_rect(LLView* parent, LLRect *rect) -{ - if (!parent) return false; - - LLView::child_list_t::const_iterator itor = - parent->getChildList()->begin(); - for (;itor != parent->getChildList()->end(); ++itor) - { - LLView *last_view = (*itor); - if (last_view->getFromXUI()) - { - *rect = last_view->getRect(); - return true; - } - } - return false; -} - -//static -void LLView::applyXUILayout(LLView::Params& p, LLView* parent, LLRect layout_rect) -{ - if (!parent) return; - - const S32 VPAD = 4; - const S32 MIN_WIDGET_HEIGHT = 10; - - // *NOTE: This will confuse export of floater/panel coordinates unless - // the default is also "topleft". JC - if (p.layout().empty()) - { - p.layout = parent->getLayout(); - } - - if (layout_rect.isEmpty()) - { - layout_rect = parent->getLocalRect(); - } - - // overwrite uninitialized rect params, using context - LLRect default_rect = parent->getLocalRect(); - - bool layout_topleft = (p.layout() == "topleft"); - - // convert negative or centered coordinates to parent relative values - // Note: some of this logic matches the logic in TypedParam::setValueFromBlock() - if (p.rect.left.isProvided()) - { - p.rect.left = p.rect.left + ((p.rect.left >= 0) ? layout_rect.mLeft : layout_rect.mRight); - } - if (p.rect.right.isProvided()) - { - p.rect.right = p.rect.right + ((p.rect.right >= 0) ? layout_rect.mLeft : layout_rect.mRight); - } - if (p.rect.bottom.isProvided()) - { - p.rect.bottom = p.rect.bottom + ((p.rect.bottom >= 0) ? layout_rect.mBottom : layout_rect.mTop); - if (layout_topleft) - { - //invert top to bottom - p.rect.bottom = layout_rect.mBottom + layout_rect.mTop - p.rect.bottom; - } - } - if (p.rect.top.isProvided()) - { - p.rect.top = p.rect.top + ((p.rect.top >= 0) ? layout_rect.mBottom : layout_rect.mTop); - if (layout_topleft) - { - //invert top to bottom - p.rect.top = layout_rect.mBottom + layout_rect.mTop - p.rect.top; - } - } - - // DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels - if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0) - { - p.rect.height = MIN_WIDGET_HEIGHT; - } - - default_rect.translate(0, default_rect.getHeight()); - - // If there was a recently constructed child, use its rectangle - get_last_child_rect(parent, &default_rect); - - if (layout_topleft) - { - // Invert the sense of bottom_delta for topleft layout - if (p.bottom_delta.isProvided()) - { - p.bottom_delta = -p.bottom_delta; - } - else if (p.top_pad.isProvided()) - { - p.bottom_delta = -(p.rect.height + p.top_pad); - } - else if (p.top_delta.isProvided()) - { - p.bottom_delta = - -(p.top_delta + p.rect.height - default_rect.getHeight()); - } - else if (!p.left_delta.isProvided() - && !p.left_pad.isProvided()) - { - // set default position is just below last rect - p.bottom_delta.set(-(p.rect.height + VPAD), false); - } - else - { - p.bottom_delta.set(0, false); - } - - // default to same left edge - if (!p.left_delta.isProvided()) - { - p.left_delta.set(0, false); - } - if (p.left_pad.isProvided()) - { - // left_pad is based on prior widget's right edge - p.left_delta.set(p.left_pad + default_rect.getWidth(), false); - } - - default_rect.translate(p.left_delta, p.bottom_delta); - } - else - { - // set default position is just below last rect - if (!p.bottom_delta.isProvided()) - { - p.bottom_delta.set(-(p.rect.height + VPAD), false); - } - if (!p.left_delta.isProvided()) - { - p.left_delta.set(0, false); - } - default_rect.translate(p.left_delta, p.bottom_delta); - } - - // this handles case where *both* x and x_delta are provided - // ignore x in favor of default x + x_delta - if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false); - if (p.left_delta.isProvided()) p.rect.left.set(0, false); - - // selectively apply rectangle defaults, making sure that - // params are not flagged as having been "provided" - // as rect params are overconstrained and rely on provided flags - if (!p.rect.left.isProvided()) - { - p.rect.left.set(default_rect.mLeft, false); - //HACK: get around the fact that setting a rect param component value won't invalidate the existing rect object value - p.rect.paramChanged(p.rect.left, true); - } - if (!p.rect.bottom.isProvided()) - { - p.rect.bottom.set(default_rect.mBottom, false); - p.rect.paramChanged(p.rect.bottom, true); - } - if (!p.rect.top.isProvided()) - { - p.rect.top.set(default_rect.mTop, false); - p.rect.paramChanged(p.rect.top, true); - } - if (!p.rect.right.isProvided()) - { - p.rect.right.set(default_rect.mRight, false); - p.rect.paramChanged(p.rect.right, true); - - } - if (!p.rect.width.isProvided()) - { - p.rect.width.set(default_rect.getWidth(), false); - p.rect.paramChanged(p.rect.width, true); - } - if (!p.rect.height.isProvided()) - { - p.rect.height.set(default_rect.getHeight(), false); - p.rect.paramChanged(p.rect.height, true); - } -} - -static S32 invert_vertical(S32 y, LLView* parent) -{ - if (y < 0) - { - // already based on top-left, just invert - return -y; - } - else if (parent) - { - // use parent to flip coordinate - S32 parent_height = parent->getRect().getHeight(); - return parent_height - y; - } - else - { - LL_WARNS() << "Attempting to convert layout to top-left with no parent" << LL_ENDL; - return y; - } -} - -// Assumes that input is in bottom-left coordinates, hence must call -// _before_ convert_coords_to_top_left(). -static void convert_to_relative_layout(LLView::Params& p, LLView* parent) -{ - // Use setupParams to get the final widget rectangle - // according to our wacky layout rules. - LLView::Params final = p; - LLView::applyXUILayout(final, parent); - // Must actually extract the rectangle to get consistent - // right = left+width, top = bottom+height - LLRect final_rect = final.rect; - - // We prefer to write out top edge instead of bottom, regardless - // of whether we use relative positioning - bool converted_top = false; - - // Look for a last rectangle - LLRect last_rect; - if (get_last_child_rect(parent, &last_rect)) - { - // ...we have a previous widget to compare to - const S32 EDGE_THRESHOLD_PIXELS = 4; - S32 left_pad = final_rect.mLeft - last_rect.mRight; - S32 left_delta = final_rect.mLeft - last_rect.mLeft; - S32 top_pad = final_rect.mTop - last_rect.mBottom; - S32 top_delta = final_rect.mTop - last_rect.mTop; - // If my left edge is almost the same, or my top edge is - // almost the same... - if (llabs(left_delta) <= EDGE_THRESHOLD_PIXELS - || llabs(top_delta) <= EDGE_THRESHOLD_PIXELS) - { - // ...use relative positioning - // prefer top_pad if widgets are stacking vertically - // (coordinate system is still bottom-left here) - if (top_pad < 0) - { - p.top_pad = top_pad; - p.top_delta.setProvided(false); - } - else - { - p.top_pad.setProvided(false); - p.top_delta = top_delta; - } - // null out other vertical specifiers - p.rect.top.setProvided(false); - p.rect.bottom.setProvided(false); - p.bottom_delta.setProvided(false); - converted_top = true; - - // prefer left_pad if widgets are stacking horizontally - if (left_pad > 0) - { - p.left_pad = left_pad; - p.left_delta.setProvided(false); - } - else - { - p.left_pad.setProvided(false); - p.left_delta = left_delta; - } - p.rect.left.setProvided(false); - p.rect.right.setProvided(false); - } - } - - if (!converted_top) - { - // ...this is the first widget, or one that wasn't aligned - // prefer top/left specification - p.rect.top = final_rect.mTop; - p.rect.bottom.setProvided(false); - p.bottom_delta.setProvided(false); - p.top_pad.setProvided(false); - p.top_delta.setProvided(false); - } -} - -static void convert_coords_to_top_left(LLView::Params& p, LLView* parent) -{ - // Convert the coordinate system to be top-left based. - if (p.rect.top.isProvided()) - { - p.rect.top = invert_vertical(p.rect.top, parent); - } - if (p.rect.bottom.isProvided()) - { - p.rect.bottom = invert_vertical(p.rect.bottom, parent); - } - if (p.top_pad.isProvided()) - { - p.top_pad = -p.top_pad; - } - if (p.top_delta.isProvided()) - { - p.top_delta = -p.top_delta; - } - if (p.bottom_delta.isProvided()) - { - p.bottom_delta = -p.bottom_delta; - } - p.layout = "topleft"; -} - -//static -void LLView::setupParamsForExport(Params& p, LLView* parent) -{ - // Don't convert if already top-left based - if (p.layout() == "topleft") - { - return; - } - - // heuristic: Many of our floaters and panels were bulk-exported. - // These specify exactly bottom/left and height/width. - // Others were done by hand using bottom_delta and/or left_delta. - // Some rely on not specifying left to mean align with left edge. - // Try to convert both to use relative layout, but using top-left - // coordinates. - // Avoid rectangles where top/bottom/left/right was specified. - if (p.rect.height.isProvided() && p.rect.width.isProvided()) - { - if (p.rect.bottom.isProvided() && p.rect.left.isProvided()) - { - // standard bulk export, convert it - convert_to_relative_layout(p, parent); - } - else if (p.rect.bottom.isProvided() && p.left_delta.isProvided()) - { - // hand layout with left_delta - convert_to_relative_layout(p, parent); - } - else if (p.bottom_delta.isProvided()) - { - // hand layout with bottom_delta - // don't check for p.rect.left or p.left_delta because sometimes - // this layout doesn't set it for widgets that are left-aligned - convert_to_relative_layout(p, parent); - } - } - - convert_coords_to_top_left(p, parent); -} - -LLView::tree_iterator_t LLView::beginTreeDFS() -{ - return tree_iterator_t(this, - boost::bind(boost::mem_fn(&LLView::beginChild), _1), - boost::bind(boost::mem_fn(&LLView::endChild), _1)); -} - -LLView::tree_iterator_t LLView::endTreeDFS() -{ - // an empty iterator is an "end" iterator - return tree_iterator_t(); -} - -LLView::tree_post_iterator_t LLView::beginTreeDFSPost() -{ - return tree_post_iterator_t(this, - boost::bind(boost::mem_fn(&LLView::beginChild), _1), - boost::bind(boost::mem_fn(&LLView::endChild), _1)); -} - -LLView::tree_post_iterator_t LLView::endTreeDFSPost() -{ - // an empty iterator is an "end" iterator - return tree_post_iterator_t(); -} - -LLView::bfs_tree_iterator_t LLView::beginTreeBFS() -{ - return bfs_tree_iterator_t(this, - boost::bind(boost::mem_fn(&LLView::beginChild), _1), - boost::bind(boost::mem_fn(&LLView::endChild), _1)); -} - -LLView::bfs_tree_iterator_t LLView::endTreeBFS() -{ - // an empty iterator is an "end" iterator - return bfs_tree_iterator_t(); -} - - -LLView::root_to_view_iterator_t LLView::beginRootToView() -{ - return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1)); -} - -LLView::root_to_view_iterator_t LLView::endRootToView() -{ - return root_to_view_iterator_t(); -} - - -// only create maps on demand, as they incur heap allocation/deallocation cost -// when a view is constructed/deconstructed -LLView& LLView::getDefaultWidgetContainer() const -{ - if (!mDefaultWidgets) - { - LLView::Params p; - p.name = "default widget container"; - p.visible = false; // ensures default widgets can't steal focus, etc. - mDefaultWidgets = new LLView(p); - } - return *mDefaultWidgets; -} - -S32 LLView::notifyParent(const LLSD& info) -{ - LLView* parent = getParent(); - if(parent) - return parent->notifyParent(info); - return 0; -} -bool LLView::notifyChildren(const LLSD& info) -{ - bool ret = false; - for (LLView* childp : mChildList) - { - ret = ret || childp->notifyChildren(info); - } - return ret; -} - -// convenient accessor for draw context -const LLViewDrawContext& LLView::getDrawContext() -{ - return LLViewDrawContext::getCurrentContext(); -} - -const LLViewDrawContext& LLViewDrawContext::getCurrentContext() -{ - static LLViewDrawContext default_context; - - if (sDrawContextStack.empty()) - return default_context; - - return *sDrawContextStack.back(); -} - -LLSD LLView::getInfo(void) -{ - LLSD info; - addInfo(info); - return info; -} - -void LLView::addInfo(LLSD & info) -{ - info["path"] = getPathname(); - info["class"] = typeid(*this).name(); - info["visible"] = getVisible(); - info["visible_chain"] = isInVisibleChain(); - info["enabled"] = getEnabled(); - info["enabled_chain"] = isInEnabledChain(); - info["available"] = isAvailable(); - LLRect rect(calcScreenRect()); - info["rect"] = LLSDMap("left", rect.mLeft)("top", rect.mTop) - ("right", rect.mRight)("bottom", rect.mBottom); -} +/** + * @file llview.cpp + * @author James Cook + * @brief Container for other views, anything that draws. + * + * $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 "linden_common.h" + +#define LLVIEW_CPP +#include "llview.h" + +#include +#include +#include + +#include "llrender.h" +#include "llevent.h" +#include "llfocusmgr.h" +#include "llrect.h" +#include "llstl.h" +#include "llui.h" +#include "lluictrl.h" +#include "llwindow.h" +#include "v3color.h" +#include "lluictrlfactory.h" +#include "lltooltip.h" +#include "llsdutil.h" +#include "llsdserialize.h" +#include "llviewereventrecorder.h" +#include "llkeyboard.h" +// for ui edit hack +#include "llbutton.h" +#include "lllineeditor.h" +#include "lltexteditor.h" +#include "lltextbox.h" + +static const S32 LINE_HEIGHT = 15; + +S32 LLView::sDepth = 0; +bool LLView::sDebugRects = false; +bool LLView::sDebugUnicode = false; +bool LLView::sDebugCamera = false; +bool LLView::sIsRectDirty = false; +LLRect LLView::sDirtyRect; +bool LLView::sDebugRectsShowNames = true; +bool LLView::sDebugKeys = false; +bool LLView::sDebugMouseHandling = false; +std::string LLView::sMouseHandlerMessage; +bool LLView::sForceReshape = false; +std::set LLView::sPreviewHighlightedElements; +bool LLView::sHighlightingDiffs = false; +LLView* LLView::sPreviewClickedElement = NULL; +bool LLView::sDrawPreviewHighlights = false; +S32 LLView::sLastLeftXML = S32_MIN; +S32 LLView::sLastBottomXML = S32_MIN; +std::vector LLViewDrawContext::sDrawContextStack; + +LLView::DrilldownFunc LLView::sDrilldown = + boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT); + +//#if LL_DEBUG +bool LLView::sIsDrawing = false; +//#endif + +// Compiler optimization, generate extern template +template class LLView* LLView::getChild( + const std::string& name, bool recurse) const; + +static LLDefaultChildRegistry::Register r("view"); + +void deleteView(LLView *aView) +{ + delete aView; +} + +namespace LLInitParam +{ + void TypeValues::declareValues() + { + declare("horizontal", LLView::HORIZONTAL); + declare("vertical", LLView::VERTICAL); + } +} + + +LLView::Follows::Follows() +: string(""), + flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP) +{} + +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", false), + 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 LLView::Params& p) +: mVisible(p.visible), + mInDraw(false), + mName(p.name), + mParentView(NULL), + mReshapeFlags(FOLLOWS_NONE), + mFromXUI(p.from_xui), + mIsFocusRoot(p.focus_root), + mLastVisible(false), + 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); + parseFollowsFlags(p); +} + +LLView::~LLView() +{ + dirtyRect(); + //LL_INFOS() << "Deleting view " << mName << ":" << (void*) this << LL_ENDL; + if (LLView::sIsDrawing) + { + LL_DEBUGS() << "Deleting view " << mName << " during UI draw() phase" << LL_ENDL; + } +// llassert(LLView::sIsDrawing == false); + +// llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators + + if( hasMouseCapture() ) + { + //LL_WARNS() << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << LL_ENDL; + gFocusMgr.removeMouseCaptureWithoutCallback( this ); + } + + deleteAllChildren(); + + if (mParentView != NULL) + { + mParentView->removeChild(this); + } + + if (mDefaultWidgets) + { + delete mDefaultWidgets; + mDefaultWidgets = NULL; + } +} + +// virtual +bool LLView::isCtrl() const +{ + return false; +} + +// virtual +bool LLView::isPanel() const +{ + return false; +} + +void LLView::setToolTip(const LLStringExplicit& msg) +{ + mToolTipMsg = msg; +} + +bool LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text) +{ + mToolTipMsg.setArg(key, text); + return true; +} + +void LLView::setToolTipArgs( const LLStringUtil::format_map_t& args ) +{ + mToolTipMsg.setArgList(args); +} + +// virtual +void LLView::setRect(const LLRect& rect) +{ + mRect = rect; + updateBoundingRect(); +} + +void LLView::setUseBoundingRect( bool use_bounding_rect ) +{ + if (mUseBoundingRect != use_bounding_rect) + { + mUseBoundingRect = use_bounding_rect; + updateBoundingRect(); + } +} + +bool LLView::getUseBoundingRect() const +{ + return mUseBoundingRect; +} + +// virtual +const std::string& LLView::getName() const +{ + static std::string no_name("(no name)"); + + return mName.empty() ? no_name : mName; +} + +void LLView::sendChildToFront(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, + // won't temporarily create an empty list + if (child != mChildList.front()) + { + mChildList.remove( child ); + mChildList.push_front(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, + // won't temporarily create an empty list + if (child != mChildList.back()) + { + mChildList.remove( child ); + mChildList.push_back(child); + } + } +} + +// virtual +bool LLView::addChild(LLView* child, S32 tab_group) +{ + if (!child) + { + return false; + } + + if (this == child) + { + LL_ERRS() << "Adding view " << child->getName() << " as child of itself" << LL_ENDL; + } + + // remove from current parent + if (child->mParentView) + { + child->mParentView->removeChild(child); + } + + // add to front of child list, as normal + mChildList.push_front(child); + + // add to tab order list + if (tab_group != 0) + { + mTabOrder.insert(tab_order_pair_t(child, tab_group)); + } + + child->mParentView = this; + if (getVisible() && child->getVisible()) + { + // if child isn't visible it won't affect bounding rect + // if current view is not visible it will be recalculated + // on visibility change + updateBoundingRect(); + } + mLastTabGroup = tab_group; + return true; +} + + +bool LLView::addChildInBack(LLView* child, S32 tab_group) +{ + if(addChild(child, tab_group)) + { + sendChildToBack(child); + return true; + } + + return false; +} + +// remove the specified child from the view, and set it's parent to NULL. +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); + mChildList.remove( child ); + child->mParentView = NULL; + child_tab_order_t::iterator found = mTabOrder.find(child); + if (found != mTabOrder.end()) + { + mTabOrder.erase(found); + } + } + else + { + LL_WARNS() << "\"" << child->getName() << "\" is not a child of " << getName() << LL_ENDL; + } + updateBoundingRect(); +} + +bool LLView::isInVisibleChain() const +{ + bool visible = true; + + const LLView* viewp = this; + while(viewp) + { + if (!viewp->getVisible()) + { + visible = false; + break; + } + viewp = viewp->getParent(); + } + + return visible; +} + +bool LLView::isInEnabledChain() const +{ + bool enabled = true; + + const LLView* viewp = this; + while(viewp) + { + if (!viewp->getEnabled()) + { + enabled = false; + break; + } + viewp = viewp->getParent(); + } + + return enabled; +} + +static void buildPathname(std::ostream& out, const LLView* view) +{ + if (! (view && view->getParent())) + { + return; // Don't include root in the path. + } + + buildPathname(out, view->getParent()); + + // Build pathname into ostream on the way back from recursion. + out << '/'; + + // substitute all '/' in name with appropriate code + std::string name = view->getName(); + std::size_t found = name.find('/'); + std::size_t start = 0; + while (found != std::string::npos) + { + std::size_t sub_len = found - start; + if (sub_len > 0) + { + out << name.substr(start, sub_len); + } + out << "%2F"; + start = found + 1; + found = name.find('/', start); + } + if (start < name.size()) + { + out << name.substr(start, name.size() - start); + } +} + +std::string LLView::getPathname() const +{ + std::ostringstream out; + buildPathname(out, this); + return out.str(); +} + +//static +std::string LLView::getPathname(const LLView* view) +{ + if (! view) + { + return "NULL"; + } + return view->getPathname(); +} + +// virtual +bool LLView::canFocusChildren() const +{ + return true; +} + +//virtual +void LLView::setEnabled(bool enabled) +{ + mEnabled = enabled; +} + +//virtual +bool LLView::isAvailable() const +{ + return isInEnabledChain() && isInVisibleChain(); +} + +//static +bool LLView::isAvailable(const LLView* view) +{ + return view && view->isAvailable(); +} + +//virtual +bool LLView::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + return false; +} + +//virtual +LLRect LLView::getSnapRect() const +{ + return mRect; +} + +//virtual +LLRect LLView::getRequiredRect() +{ + return mRect; +} + +bool LLView::focusNextRoot() +{ + LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); + return LLView::focusNext(result); +} + +bool LLView::focusPrevRoot() +{ + LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); + return LLView::focusPrev(result); +} + +// static +bool LLView::focusNext(LLView::child_list_t & result) +{ + LLView::child_list_reverse_iter_t focused = result.rend(); + for(LLView::child_list_reverse_iter_t iter = result.rbegin(); + iter != result.rend(); + ++iter) + { + if(gFocusMgr.childHasKeyboardFocus(*iter)) + { + focused = iter; + break; + } + } + LLView::child_list_reverse_iter_t next = focused; + next = (next == result.rend()) ? result.rbegin() : ++next; + while(next != focused) + { + // wrap around to beginning if necessary + if(next == result.rend()) + { + next = result.rbegin(); + } + if ((*next)->isCtrl() && ((LLUICtrl*)*next)->hasTabStop()) + { + LLUICtrl * ctrl = static_cast(*next); + ctrl->setFocus(true); + ctrl->onTabInto(); + gFocusMgr.triggerFocusFlash(); + return true; + } + ++next; + } + return false; +} + +// static +bool LLView::focusPrev(LLView::child_list_t & result) +{ + LLView::child_list_iter_t focused = result.end(); + for(LLView::child_list_iter_t iter = result.begin(); + iter != result.end(); + ++iter) + { + if(gFocusMgr.childHasKeyboardFocus(*iter)) + { + focused = iter; + break; + } + } + LLView::child_list_iter_t next = focused; + next = (next == result.end()) ? result.begin() : ++next; + while(next != focused) + { + // wrap around to beginning if necessary + if(next == result.end()) + { + next = result.begin(); + } + if((*next)->isCtrl()) + { + LLUICtrl * ctrl = static_cast(*next); + if (!ctrl->hasFocus()) + { + ctrl->setFocus(true); + ctrl->onTabInto(); + gFocusMgr.triggerFocusFlash(); + } + return true; + } + ++next; + } + return false; +} + +// delete all children. Override this function if you need to +// perform any extra clean up such as cached pointers to selected +// children, etc. +void LLView::deleteAllChildren() +{ + // clear out the control ordering + mTabOrder.clear(); + + while (!mChildList.empty()) + { + LLView* viewp = mChildList.front(); + viewp->mParentView = NULL; + delete viewp; + mChildList.pop_front(); + } + updateBoundingRect(); +} + +void LLView::setAllChildrenEnabled(bool b) +{ + for (LLView* viewp : mChildList) + { + viewp->setEnabled(b); + } +} + +// virtual +void LLView::setVisible(bool visible) +{ + if ( mVisible != visible ) + { + mVisible = visible; + + // notify children of visibility change if root, or part of visible hierarchy + if (!getParent() || getParent()->isInVisibleChain()) + { + // tell all children of this view that the visibility may have changed + dirtyRect(); + onVisibilityChange( visible ); + } + updateBoundingRect(); + } +} + +// virtual +void LLView::onVisibilityChange ( bool new_visibility ) +{ + bool old_visibility; + bool log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus(); + for (LLView* viewp : mChildList) + { + if (!viewp) + { + continue; + } + + // only views that are themselves visible will have their overall visibility affected by their ancestors + old_visibility=viewp->getVisible(); + + if(log_visibility_change) + { + if (old_visibility!=new_visibility) + { + LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget"); + } + } + + if (old_visibility) + { + viewp->onVisibilityChange ( new_visibility ); + } + + if(log_visibility_change) + { + // Consider changing returns to confirm success and know which widget grabbed it + // For now assume success and log at highest xui possible + // NOTE we log actual state - which may differ if it somehow failed to set visibility + LL_DEBUGS() << "LLView::handleVisibilityChange - now: " << getVisible() << " xui: " << viewp->getPathname() << " name: " << viewp->getName() << LL_ENDL; + + } + } +} + +// virtual +void LLView::onUpdateScrollToChild(const LLUICtrl * cntrl) +{ + LLView* parent_view = getParent(); + if (parent_view) + { + parent_view->onUpdateScrollToChild(cntrl); + } +} + +// virtual +void LLView::translate(S32 x, S32 y) +{ + mRect.translate(x, y); + updateBoundingRect(); +} + +// virtual +bool LLView::canSnapTo(const LLView* other_view) +{ + return other_view != this && other_view->getVisible(); +} + +// virtual +void LLView::setSnappedTo(const LLView* snap_view) +{ +} + +bool LLView::handleHover(S32 x, S32 y, MASK mask) +{ + return childrenHandleHover( x, y, mask ) != NULL; +} + +void LLView::onMouseEnter(S32 x, S32 y, MASK mask) +{ + //LL_INFOS() << "Mouse entered " << getName() << LL_ENDL; +} + +void LLView::onMouseLeave(S32 x, S32 y, MASK mask) +{ + //LL_INFOS() << "Mouse left " << getName() << LL_ENDL; +} + +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(); +} + +// This is NOT event recording related +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() ) + { + for (LLView* viewp : mChildList) + { + if ((viewp->*method)(c, mask, true)) + { + if (LLView::sDebugKeys) + { + LL_INFOS() << desc << " handled by " << viewp->getName() << LL_ENDL; + } + 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) +{ + for (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 ))) + { + LL_DEBUGS() << "LLView::childrenHandleMouseEvent calling updatemouseeventinfo - local_x|global x "<< local_x << " " << x << "local/global y " << local_y << " " << y << LL_ENDL; + LL_DEBUGS() << "LLView::childrenHandleMouseEvent getPathname for viewp result: " << viewp->getPathname() << "for this view: " << getPathname() << LL_ENDL; + + LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); + + // This is NOT event recording related + viewp->logMouseEvent(); + + return viewp; + } + } + return NULL; +} + +LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask) +{ + for (LLView* viewp : mChildList) + { + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + // Differs from childrenHandleMouseEvent() in that we want to offer + // tooltips even for disabled widgets. + if(!viewp->visibleAndContains(local_x, local_y)) + { + continue; + } + + if (viewp->handleToolTip(local_x, local_y, mask) + || viewp->blockMouseEvent(local_x, local_y)) + { + // This is NOT event recording related + 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; + + for (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) +{ + for (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::getInstance()->mWindow->setCursor(viewp->getHoverCursor()); + + if (viewp->handleHover(local_x, local_y, mask) + || viewp->blockMouseEvent(local_x, local_y)) + { + // This is NOT event recording related + viewp->logMouseEvent(); + return viewp; + } + } + return NULL; +} + +LLView* LLView::childFromPoint(S32 x, S32 y, bool recur) +{ + if (!getVisible()) + return NULL; + + for (LLView* viewp : mChildList) + { + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if (!viewp->visibleAndContains(local_x, local_y)) + { + continue; + } + // Here we've found the first (frontmost) visible child at this level + // containing the specified point. Is the caller asking us to drill + // down and return the innermost leaf child at this point, or just the + // top-level child? + if (recur) + { + LLView* leaf(viewp->childFromPoint(local_x, local_y, recur)); + // Maybe viewp is already a leaf LLView, or maybe it has children + // but this particular (x, y) point falls between them. If the + // recursive call returns non-NULL, great, use that; else just use + // viewp. + return leaf? leaf : viewp; + } + return viewp; + + } + return 0; +} + +F32 LLView::getTooltipTimeout() +{ + static LLCachedControl tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f); + static LLCachedControl tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f); + // allow "scrubbing" over ui by showing next tooltip immediately + // if previous one was still visible + return (F32)(LLToolTipMgr::instance().toolTipVisible() + ? tooltip_fast_delay + : tooltip_delay); +} + +// virtual +const std::string LLView::getToolTip() const +{ + if (sDebugUnicode) + { + std::string text = getText(); + if (!text.empty()) + { + const std::string& name = getName(); + std::string tooltip = llformat("Name: \"%s\"", name.c_str()); + + if (const LLFontGL* font = getFont()) + { + tooltip += llformat("\nFont: %s (%s)", + font->getFontDesc().getName().c_str(), + font->getFontDesc().getSize().c_str() + ); + } + + tooltip += "\n\n" + utf8str_showBytesUTF8(text); + + return tooltip; + } + } + + return mToolTipMsg.getString(); +} + +bool LLView::handleToolTip(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // parents provide tooltips first, which are optionally + // overridden by children, in case child is mouse_opaque + std::string tooltip = getToolTip(); + if (!tooltip.empty()) + { + static LLCachedControl allow_ui_tooltips(*LLUI::getInstance()->mSettingGroups["config"], "BasicUITooltips", true); + + // Even if we don't show tooltips, consume the event, nothing below should show tooltip + if (allow_ui_tooltips) + { + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(tooltip) + .sticky_rect(calcScreenRect()) + .delay_time(getTooltipTimeout())); + } + handled = true; + } + + // child tooltips will override our own + LLView* child_handler = childrenHandleToolTip(x, y, mask); + if (child_handler) + { + handled = true; + } + + return handled; +} + +bool LLView::handleKey(KEY key, MASK mask, bool called_from_parent) +{ + bool handled = false; + + if (getVisible() && getEnabled()) + { + if( called_from_parent ) + { + // Downward traversal + handled = childrenHandleKey( key, mask ) != NULL; + } + + if (!handled) + { + // For event logging we don't care which widget handles it + // So we capture the key at the end of this function once we know if it was handled + handled = handleKeyHere( key, mask ); + if (handled) + { + LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL; + } + } + } + + if( !handled && !called_from_parent && mParentView) + { + // Upward traversal + handled = mParentView->handleKey( key, mask, false ); + } + return handled; +} + +bool LLView::handleKeyUp(KEY key, MASK mask, bool called_from_parent) +{ + bool handled = false; + + if (getVisible() && getEnabled()) + { + if (called_from_parent) + { + // Downward traversal + handled = childrenHandleKeyUp(key, mask) != NULL; + } + + if (!handled) + { + // For event logging we don't care which widget handles it + // So we capture the key at the end of this function once we know if it was handled + handled = handleKeyUpHere(key, mask); + if (handled) + { + LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL; + } + } + } + + if (!handled && !called_from_parent && mParentView) + { + // Upward traversal + handled = mParentView->handleKeyUp(key, mask, false); + } + return handled; +} + +// Called from handleKey() +// Handles key in this object. Checking parents and children happens in handleKey() +bool LLView::handleKeyHere(KEY key, MASK mask) +{ + return false; +} + +// Called from handleKey() +// Handles key in this object. Checking parents and children happens in handleKey() +bool LLView::handleKeyUpHere(KEY key, MASK mask) +{ + return false; +} + +bool LLView::handleUnicodeChar(llwchar uni_char, bool called_from_parent) +{ + bool handled = false; + + if (getVisible() && getEnabled()) + { + if( called_from_parent ) + { + // Downward traversal + handled = childrenHandleUnicodeChar( uni_char ) != NULL; + } + + if (!handled) + { + handled = handleUnicodeCharHere(uni_char); + if (handled && LLView::sDebugKeys) + { + LL_INFOS() << "Unicode key " << wchar_utf8_preview(uni_char) << " is handled by " << getName() << LL_ENDL; + } + } + } + + if (!handled && !called_from_parent && mParentView) + { + // Upward traversal + handled = mParentView->handleUnicodeChar(uni_char, false); + } + + if (handled) + { + LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char); + } + + return handled; +} + + +bool LLView::handleUnicodeCharHere(llwchar uni_char ) +{ + return false; +} + + +bool LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, + EDragAndDropType cargo_type, void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; +} + +void LLView::onMouseCaptureLost() +{ +} + +bool LLView::hasMouseCapture() +{ + return gFocusMgr.getMouseCapture() == this; +} + +bool LLView::handleMouseUp(S32 x, S32 y, MASK mask) +{ + LLView* r = childrenHandleMouseUp( x, y, mask ); + + return (r!=NULL); +} + +bool LLView::handleMouseDown(S32 x, S32 y, MASK mask) +{ + LLView* r= childrenHandleMouseDown(x, y, mask ); + + return (r!=NULL); +} + +bool LLView::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + return childrenHandleDoubleClick( x, y, mask ) != NULL; +} + +bool LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + return childrenHandleScrollWheel( x, y, clicks ) != NULL; +} + +bool LLView::handleScrollHWheel(S32 x, S32 y, S32 clicks) +{ + return childrenHandleScrollHWheel( x, y, clicks ) != NULL; +} + +bool LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + return childrenHandleRightMouseDown( x, y, mask ) != NULL; +} + +bool LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + return childrenHandleRightMouseUp( x, y, mask ) != NULL; +} + +bool LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + return childrenHandleMiddleMouseDown( x, y, mask ) != NULL; +} + +bool LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) +{ + return childrenHandleMiddleMouseUp( x, y, mask ) != NULL; +} + +LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks) +{ + return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false); +} + +LLView* LLView::childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks) +{ + return childrenHandleMouseEvent(&LLView::handleScrollHWheel, x, y, clicks, false); +} + +// Called during downward traversal +LLView* LLView::childrenHandleKey(KEY key, MASK mask) +{ + return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask); +} + +// Called during downward traversal +LLView* LLView::childrenHandleKeyUp(KEY key, MASK mask) +{ + return childrenHandleCharEvent("Key Up", &LLView::handleKeyUp, key, mask); +} + +// Called during downward traversal +LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char) +{ + return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask, + uni_char, MASK_NONE); +} + +LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask) +{ + return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask); +} + +LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask) +{ + return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask); +} + +LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask); +} + +LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask) +{ + return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask); +} + +LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask) +{ + return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask); +} + +LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask) +{ + return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask); +} + +LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) +{ + return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask); +} + +void LLView::draw() +{ + drawChildren(); +} + +void LLView::drawChildren() +{ + if (!mChildList.empty()) + { + LLView* rootp = LLUI::getInstance()->getRootView(); + ++sDepth; + + for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter) + { + child_list_reverse_iter_t child = child_iter++; + LLView *viewp = *child; + + if (viewp == NULL) + { + continue; + } + + if (viewp->getVisible() && viewp->getRect().isValid()) + { + LLRect screen_rect = viewp->calcScreenRect(); + if ( rootp->getLocalRect().overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect)) + { + LLUI::pushMatrix(); + { + LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom); + // 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()) + { + LL_WARNS() << "Bogus rectangle for " << getName() << " with " << mRect << LL_ENDL; + } + } + } + LLUI::popMatrix(); + } + } + + } + --sDepth; + } +} + +void LLView::dirtyRect() +{ + LLView* child = getParent(); + LLView* parent = child ? child->getParent() : NULL; + LLView* cur = this; + while (child && parent && parent->getParent()) + { //find third to top-most view + cur = child; + child = parent; + parent = parent->getParent(); + } + + if (!sIsRectDirty) + { + sDirtyRect = cur->calcScreenRect(); + sIsRectDirty = true; + } + else + { + sDirtyRect.unionWith(cur->calcScreenRect()); + } +} + +//Draw a box for debugging. +void LLView::drawDebugRect() +{ + std::set::iterator preview_iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); // figure out if it's a previewed element + + LLUI::pushMatrix(); + { + // drawing solids requires texturing be disabled + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + if (getUseBoundingRect()) + { + LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom); + } + + LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect; + + // draw red rectangle for the border + LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f); + if(preview_iter != sPreviewHighlightedElements.end()) + { + if(LLView::sPreviewClickedElement && this == sPreviewClickedElement) + { + border_color = LLColor4::red; + } + else + { + static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor"); + border_color = scroll_highlighted_color; + } + } + else + { + border_color.mV[sDepth%3] = 1.f; + } + + gGL.color4fv( border_color.mV ); + + gGL.begin(LLRender::LINES); + gGL.vertex2i(0, debug_rect.getHeight() - 1); + gGL.vertex2i(0, 0); + + gGL.vertex2i(0, 0); + gGL.vertex2i(debug_rect.getWidth() - 1, 0); + + gGL.vertex2i(debug_rect.getWidth() - 1, 0); + gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); + + gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); + gGL.vertex2i(0, debug_rect.getHeight() - 1); + gGL.end(); + + // Draw the name if it's not a leaf node or not in editing or preview mode + if (mChildList.size() + && preview_iter == sPreviewHighlightedElements.end() + && sDebugRectsShowNames) + { + S32 x, y; + gGL.color4fv( border_color.mV ); + + x = debug_rect.getWidth() / 2; + + S32 rect_height = debug_rect.getHeight(); + S32 lines = rect_height / LINE_HEIGHT + 1; + + S32 depth = 0; + LLView * viewp = this; + while (NULL != viewp) + { + viewp = viewp->getParent(); + depth++; + } + + y = rect_height - LINE_HEIGHT * (depth % lines + 1); + + std::string debug_text = llformat("%s (%d x %d)", getName().c_str(), + debug_rect.getWidth(), debug_rect.getHeight()); + LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, + LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW); + } + } + LLUI::popMatrix(); +} + +void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, bool force_draw) +{ + if (childp && childp->getParent() == this) + { + ++sDepth; + + if ((childp->getVisible() && childp->getRect().isValid()) + || force_draw) + { + gGL.matrixMode(LLRender::MM_MODELVIEW); + LLUI::pushMatrix(); + { + LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset); + childp->draw(); + } + LLUI::popMatrix(); + } + + --sDepth; + } +} + + +void LLView::reshape(S32 width, S32 height, bool called_from_parent) +{ + // compute how much things changed and apply reshape logic to children + S32 delta_width = width - getRect().getWidth(); + S32 delta_height = height - getRect().getHeight(); + + if (delta_width || delta_height || sForceReshape) + { + // adjust our rectangle + mRect.mRight = getRect().mLeft + width; + mRect.mTop = getRect().mBottom + height; + + // move child views according to reshape flags + for (LLView* viewp : mChildList) + { + if (viewp != NULL) + { + LLRect child_rect( viewp->mRect ); + + if (viewp->followsRight() && viewp->followsLeft()) + { + child_rect.mRight += delta_width; + } + else if (viewp->followsRight()) + { + child_rect.mLeft += delta_width; + child_rect.mRight += delta_width; + } + else if (viewp->followsLeft()) + { + // left is 0, don't need to adjust coords + } + else + { + // BUG what to do when we don't follow anyone? + // for now, same as followsLeft + } + + if (viewp->followsTop() && viewp->followsBottom()) + { + child_rect.mTop += delta_height; + } + else if (viewp->followsTop()) + { + child_rect.mTop += delta_height; + child_rect.mBottom += delta_height; + } + else if (viewp->followsBottom()) + { + // bottom is 0, so don't need to adjust coords + } + else + { + // BUG what to do when we don't follow? + // for now, same as bottom + } + + S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft; + S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom; + viewp->translate( delta_x, delta_y ); + if (child_rect.getWidth() != viewp->getRect().getWidth() + || child_rect.getHeight() != viewp->getRect().getHeight() + || sForceReshape) + { + viewp->reshape(child_rect.getWidth(), child_rect.getHeight()); + } + } + } + } + + if (!called_from_parent) + { + if (mParentView) + { + mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), false); + } + } + + updateBoundingRect(); +} + +LLRect LLView::calcBoundingRect() +{ + LLRect local_bounding_rect = LLRect::null; + + for (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.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 + { + mBoundingRect = mRect; + } + + // give parent view a chance to resize, in case we just moved, for example + if (getParent() && getParent()->getUseBoundingRect()) + { + getParent()->updateBoundingRect(); + } + + if (mBoundingRect != cur_rect) + { + dirtyRect(); + } + +} + +LLRect LLView::calcScreenRect() const +{ + 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(); + local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom); + + return local_bounding_rect; +} + + +LLRect LLView::getLocalRect() const +{ + LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + return local_rect; +} + +LLRect LLView::getLocalSnapRect() const +{ + LLRect local_snap_rect = getSnapRect(); + local_snap_rect.translate(-getRect().mLeft, -getRect().mBottom); + return local_snap_rect; +} + +bool LLView::hasAncestor(const LLView* parentp) const +{ + if (!parentp) + { + return false; + } + + LLView* viewp = getParent(); + while(viewp) + { + if (viewp == parentp) + { + return true; + } + viewp = viewp->getParent(); + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool LLView::childHasKeyboardFocus( const std::string& childname ) const +{ + LLView *focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + + while (focus != NULL) + { + if (focus->getName() == childname) + { + return true; + } + + focus = focus->getParent(); + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool LLView::hasChild(const std::string& childname, bool recurse) const +{ + return findChildView(childname, recurse) != NULL; +} + +//----------------------------------------------------------------------------- +// getChildView() +//----------------------------------------------------------------------------- +LLView* LLView::getChildView(const std::string& name, bool recurse) const +{ + return getChild(name, recurse); +} + +LLView* LLView::findChildView(const std::string& name, bool recurse) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + // Look for direct children *first* + for (LLView* childp : mChildList) + { + llassert(childp); + if (childp->getName() == name) + { + return childp; + } + } + if (recurse) + { + // Look inside each child as well. + for (LLView* childp : mChildList) + { + llassert(childp); + LLView* viewp = childp->findChildView(name, recurse); + if ( viewp ) + { + return viewp; + } + } + } + return NULL; +} + +bool LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const +{ + 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 (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT) + ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) + : mRect.localPointInRect( x, y ); +} + +bool LLView::blockMouseEvent(S32 x, S32 y) const +{ + return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT); +} + +// virtual +void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const +{ + *local_x = screen_x - getRect().mLeft; + *local_y = screen_y - getRect().mBottom; + + const LLView* cur = this; + while( cur->mParentView ) + { + cur = cur->mParentView; + *local_x -= cur->getRect().mLeft; + *local_y -= cur->getRect().mBottom; + } +} + +void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const +{ + *screen_x = local_x; + *screen_y = local_y; + + const LLView* cur = this; + do + { + LLRect cur_rect = cur->getRect(); + *screen_x += cur_rect.mLeft; + *screen_y += cur_rect.mBottom; + cur = cur->mParentView; + } + while( cur ); +} + +void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const +{ + *local = screen; + local->translate( -getRect().mLeft, -getRect().mBottom ); + + const LLView* cur = this; + while( cur->mParentView ) + { + cur = cur->mParentView; + local->translate( -cur->getRect().mLeft, -cur->getRect().mBottom ); + } +} + +void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const +{ + *screen = local; + screen->translate( getRect().mLeft, getRect().mBottom ); + + const LLView* cur = this; + while( cur->mParentView ) + { + cur = cur->mParentView; + screen->translate( cur->getRect().mLeft, cur->getRect().mBottom ); + } +} + +LLView* LLView::getRootView() +{ + LLView* view = this; + while( view->mParentView ) + { + view = view->mParentView; + } + return view; +} + +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, S32 min_overlap_pixels) +{ + LLCoordGL delta; + + const S32 KEEP_ONSCREEN_PIXELS_WIDTH = llmin(min_overlap_pixels, input.getWidth()); + const S32 KEEP_ONSCREEN_PIXELS_HEIGHT = llmin(min_overlap_pixels, input.getHeight()); + + if (KEEP_ONSCREEN_PIXELS_WIDTH <= constraint.getWidth() && + KEEP_ONSCREEN_PIXELS_HEIGHT <= constraint.getHeight()) + { + if (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft) + { + delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH); + } + else if (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight) + { + delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH); + } + + if (input.mTop > constraint.mTop) + { + delta.mY = constraint.mTop - input.mTop; + } + else if (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom) + { + delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT); + } + } + + 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, S32 min_overlap_pixels) +{ + return translateRectIntoRect(getRect(), constraint, min_overlap_pixels); +} + +bool LLView::translateRectIntoRect(const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels) +{ + LLCoordGL translation = getNeededTranslation(rect, constraint, min_overlap_pixels); + + if (translation.mX != 0 || translation.mY != 0) + { + 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, S32 min_overlap_pixels) +{ + LLCoordGL translation = getNeededTranslation(getRect(), inside, min_overlap_pixels); + + 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; + S32 bottom = bounds.mBottom + (bounds.getHeight() - getRect().getHeight()) / 2; + + translate( left - getRect().mLeft, bottom - getRect().mBottom ); +} + +bool LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const +{ + const LLView* cur_view = this; + const LLView* root_view = NULL; + + while (cur_view) + { + if (cur_view == other_view) + { + *other_x = x; + *other_y = y; + return true; + } + + x += cur_view->getRect().mLeft; + y += cur_view->getRect().mBottom; + + cur_view = cur_view->getParent(); + root_view = cur_view; + } + + // assuming common root between two views, chase other_view's parents up to root + cur_view = other_view; + while (cur_view) + { + x -= cur_view->getRect().mLeft; + y -= cur_view->getRect().mBottom; + + cur_view = cur_view->getParent(); + + if (cur_view == root_view) + { + *other_x = x; + *other_y = y; + return true; + } + } + + *other_x = x; + *other_y = y; + return false; +} + +bool LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const +{ + LLRect cur_rect = local; + const LLView* cur_view = this; + const LLView* root_view = NULL; + + while (cur_view) + { + if (cur_view == other_view) + { + *other = cur_rect; + return true; + } + + cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom); + + cur_view = cur_view->getParent(); + root_view = cur_view; + } + + // assuming common root between two views, chase other_view's parents up to root + cur_view = other_view; + while (cur_view) + { + cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom); + + cur_view = cur_view->getParent(); + + if (cur_view == root_view) + { + *other = cur_rect; + return true; + } + } + + *other = cur_rect; + return false; +} + + +class CompareByTabOrder +{ +public: + CompareByTabOrder(const LLView::child_tab_order_t& order, S32 default_tab_group = 0) + : mTabOrder(order), + mDefaultTabGroup(default_tab_group) + {} + virtual ~CompareByTabOrder() {} + + // This method compares two LLViews by the tab order specified in the comparator object. The + // code for this is a little convoluted because each argument can have four states: + // 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null + bool operator() (const LLView* const a, const LLView* const b) const + { + S32 a_group = 0, b_group = 0; + if(!a) return false; + if(!b) return true; + + LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a), b_found = mTabOrder.find(b); + if(a_found != mTabOrder.end()) + { + a_group = a_found->second; + } + if(b_found != mTabOrder.end()) + { + b_group = b_found->second; + } + + if(a_group < mDefaultTabGroup && b_group >= mDefaultTabGroup) return true; + if(b_group < mDefaultTabGroup && a_group >= mDefaultTabGroup) return false; + return a_group > b_group; // sort correctly if they're both on the same side of the default tab groupreturn a > b; + } +private: + // ok to store a reference, as this should only be allocated on stack during view query operations + const LLView::child_tab_order_t& mTabOrder; + const S32 mDefaultTabGroup; +}; + +class SortByTabOrder : public LLQuerySorter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(SortByTabOrder); + /*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const override + { + children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup())); + } +}; + +// static +const LLViewQuery & LLView::getTabOrderQuery() +{ + static LLViewQuery query; + if(query.getPreFilters().size() == 0) { + query.addPreFilter(LLVisibleFilter::getInstance()); + query.addPreFilter(LLEnabledFilter::getInstance()); + query.addPreFilter(LLTabStopFilter::getInstance()); + query.addPostFilter(LLLeavesFilter::getInstance()); + query.setSorter(SortByTabOrder::getInstance()); + } + return query; +} + +// This class is only used internally by getFocusRootsQuery below. +class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override + { + return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot()); + } +}; + +// static +const LLViewQuery & LLView::getFocusRootsQuery() +{ + static LLViewQuery query; + if(query.getPreFilters().size() == 0) { + query.addPreFilter(LLVisibleFilter::getInstance()); + query.addPreFilter(LLEnabledFilter::getInstance()); + query.addPreFilter(LLFocusRootsFilter::getInstance()); + query.addPostFilter(LLRootsFilter::getInstance()); + } + return query; +} + + +void LLView::setShape(const LLRect& new_rect, bool by_user) +{ + if (new_rect != getRect()) + { + handleReshape(new_rect, by_user); + } +} + +void LLView::handleReshape(const LLRect& new_rect, bool by_user) +{ + reshape(new_rect.getWidth(), new_rect.getHeight()); + translate(new_rect.mLeft - getRect().mLeft, new_rect.mBottom - getRect().mBottom); +} + +LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, + LLView::ESnapType snap_type, S32 threshold, S32 padding) +{ + new_rect = mRect; + LLView* snap_view = NULL; + + if (!mParentView) + { + return NULL; + } + + S32 delta_x = 0; + S32 delta_y = 0; + if (mouse_dir.mX >= 0) + { + S32 new_right = mRect.mRight; + LLView* view = findSnapEdge(new_right, mouse_dir, SNAP_RIGHT, snap_type, threshold, padding); + delta_x = new_right - mRect.mRight; + snap_view = view ? view : snap_view; + } + + if (mouse_dir.mX <= 0) + { + S32 new_left = mRect.mLeft; + LLView* view = findSnapEdge(new_left, mouse_dir, SNAP_LEFT, snap_type, threshold, padding); + delta_x = new_left - mRect.mLeft; + snap_view = view ? view : snap_view; + } + + if (mouse_dir.mY >= 0) + { + S32 new_top = mRect.mTop; + LLView* view = findSnapEdge(new_top, mouse_dir, SNAP_TOP, snap_type, threshold, padding); + delta_y = new_top - mRect.mTop; + snap_view = view ? view : snap_view; + } + + if (mouse_dir.mY <= 0) + { + S32 new_bottom = mRect.mBottom; + LLView* view = findSnapEdge(new_bottom, mouse_dir, SNAP_BOTTOM, snap_type, threshold, padding); + delta_y = new_bottom - mRect.mBottom; + snap_view = view ? view : snap_view; + } + + new_rect.translate(delta_x, delta_y); + return snap_view; +} + +LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) +{ + LLRect snap_rect = getSnapRect(); + S32 snap_pos = 0; + switch(snap_edge) + { + case SNAP_LEFT: + snap_pos = snap_rect.mLeft; + break; + case SNAP_RIGHT: + snap_pos = snap_rect.mRight; + break; + case SNAP_TOP: + snap_pos = snap_rect.mTop; + break; + case SNAP_BOTTOM: + snap_pos = snap_rect.mBottom; + break; + } + + if (!mParentView) + { + new_edge_val = snap_pos; + return NULL; + } + + LLView* snap_view = NULL; + + // If the view is near the edge of its parent, snap it to + // the edge. + LLRect test_rect = snap_rect; + test_rect.stretch(padding); + + S32 x_threshold = threshold; + S32 y_threshold = threshold; + + LLRect parent_local_snap_rect = mParentView->getLocalSnapRect(); + + if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS) + { + switch(snap_edge) + { + case SNAP_RIGHT: + if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= x_threshold + && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0) + { + snap_pos = parent_local_snap_rect.mRight - padding; + snap_view = mParentView; + x_threshold = llabs(parent_local_snap_rect.mRight - test_rect.mRight); + } + break; + case SNAP_LEFT: + if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= x_threshold + && test_rect.mLeft * mouse_dir.mX <= 0) + { + snap_pos = parent_local_snap_rect.mLeft + padding; + snap_view = mParentView; + x_threshold = llabs(test_rect.mLeft - parent_local_snap_rect.mLeft); + } + break; + case SNAP_BOTTOM: + if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= y_threshold + && test_rect.mBottom * mouse_dir.mY <= 0) + { + snap_pos = parent_local_snap_rect.mBottom + padding; + snap_view = mParentView; + y_threshold = llabs(test_rect.mBottom - parent_local_snap_rect.mBottom); + } + break; + case SNAP_TOP: + if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= y_threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0) + { + snap_pos = parent_local_snap_rect.mTop - padding; + snap_view = mParentView; + y_threshold = llabs(parent_local_snap_rect.mTop - test_rect.mTop); + } + break; + default: + LL_ERRS() << "Invalid snap edge" << LL_ENDL; + } + } + + if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS) + { + for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin(); + child_it != mParentView->getChildList()->end(); ++child_it) + { + LLView* siblingp = *child_it; + + if (!canSnapTo(siblingp)) continue; + + LLRect sibling_rect = siblingp->getSnapRect(); + + switch(snap_edge) + { + case SNAP_RIGHT: + if (llabs(test_rect.mRight - sibling_rect.mLeft) <= x_threshold + && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0) + { + snap_pos = sibling_rect.mLeft - padding; + snap_view = siblingp; + x_threshold = llabs(test_rect.mRight - sibling_rect.mLeft); + } + // if snapped with sibling along other axis, check for shared edge + else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold + || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= x_threshold) + { + if (llabs(test_rect.mRight - sibling_rect.mRight) <= x_threshold + && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0) + { + snap_pos = sibling_rect.mRight; + snap_view = siblingp; + x_threshold = llabs(test_rect.mRight - sibling_rect.mRight); + } + } + break; + case SNAP_LEFT: + if (llabs(test_rect.mLeft - sibling_rect.mRight) <= x_threshold + && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0) + { + snap_pos = sibling_rect.mRight + padding; + snap_view = siblingp; + x_threshold = llabs(test_rect.mLeft - sibling_rect.mRight); + } + // if snapped with sibling along other axis, check for shared edge + else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold + || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= y_threshold) + { + if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= x_threshold + && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0) + { + snap_pos = sibling_rect.mLeft; + snap_view = siblingp; + x_threshold = llabs(test_rect.mLeft - sibling_rect.mLeft); + } + } + break; + case SNAP_BOTTOM: + if (llabs(test_rect.mBottom - sibling_rect.mTop) <= y_threshold + && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0) + { + snap_pos = sibling_rect.mTop + padding; + snap_view = siblingp; + y_threshold = llabs(test_rect.mBottom - sibling_rect.mTop); + } + // if snapped with sibling along other axis, check for shared edge + else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold + || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold) + { + if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= y_threshold + && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0) + { + snap_pos = sibling_rect.mBottom; + snap_view = siblingp; + y_threshold = llabs(test_rect.mBottom - sibling_rect.mBottom); + } + } + break; + case SNAP_TOP: + if (llabs(test_rect.mTop - sibling_rect.mBottom) <= y_threshold + && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0) + { + snap_pos = sibling_rect.mBottom - padding; + snap_view = siblingp; + y_threshold = llabs(test_rect.mTop - sibling_rect.mBottom); + } + // if snapped with sibling along other axis, check for shared edge + else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold + || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold) + { + if (llabs(test_rect.mTop - sibling_rect.mTop) <= y_threshold + && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0) + { + snap_pos = sibling_rect.mTop; + snap_view = siblingp; + y_threshold = llabs(test_rect.mTop - sibling_rect.mTop); + } + } + break; + default: + LL_ERRS() << "Invalid snap edge" << LL_ENDL; + } + } + } + + new_edge_val = snap_pos; + return snap_view; +} + +//----------------------------------------------------------------------------- +// Listener dispatch functions +//----------------------------------------------------------------------------- + + +LLControlVariable *LLView::findControl(const std::string& name) +{ + // parse the name to locate which group it belongs to + std::size_t key_pos= name.find("."); + if(key_pos!= std::string::npos ) + { + std::string control_group_key = name.substr(0, key_pos); + LLControlVariable* control; + // check if it's in the control group that name indicated + if(LLUI::getInstance()->mSettingGroups[control_group_key]) + { + control = LLUI::getInstance()->mSettingGroups[control_group_key]->getControl(name); + if (control) + { + return control; + } + } + } + + LLControlGroup& control_group = LLUI::getInstance()->getControlControlGroup(name); + return control_group.getControl(name); +} + +void LLView::initFromParams(const LLView::Params& params) +{ + LLRect required_rect = getRequiredRect(); + + S32 width = llmax(getRect().getWidth(), required_rect.getWidth()); + S32 height = llmax(getRect().getHeight(), required_rect.getHeight()); + + reshape(width, height); + + // call virtual methods with most recent data + // use getters because these values might not come through parameter block + setEnabled(getEnabled()); + setVisible(getVisible()); + + if (!params.name().empty()) + { + setName(params.name()); + } + + mLayout = params.layout(); +} + +void LLView::parseFollowsFlags(const LLView::Params& params) +{ + // preserve follows flags set by code if user did not override + if (!params.follows.isProvided()) + { + return; + } + + // interpret either string or bitfield version of follows + if (params.follows.string.isChosen()) + { + setFollows(FOLLOWS_NONE); + + std::string follows = params.follows.string; + + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("|"); + tokenizer tokens(follows, sep); + tokenizer::iterator token_iter = tokens.begin(); + + while(token_iter != tokens.end()) + { + const std::string& token_str = *token_iter; + if (token_str == "left") + { + setFollowsLeft(); + } + else if (token_str == "right") + { + setFollowsRight(); + } + else if (token_str == "top") + { + setFollowsTop(); + } + else if (token_str == "bottom") + { + setFollowsBottom(); + } + else if (token_str == "all") + { + setFollowsAll(); + } + ++token_iter; + } + } + else if (params.follows.flags.isChosen()) + { + setFollows(params.follows.flags); + } +} + + +// static +//LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node) +//{ +// LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT; +// +// if (node->hasAttribute("halign")) +// { +// std::string horizontal_align_name; +// node->getAttributeString("halign", horizontal_align_name); +// gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name); +// } +// return gl_hfont_align; +//} + +// Return the rectangle of the last-constructed child, +// if present and a first-class widget (eg, not a close box or drag handle) +// Returns true if found +static bool get_last_child_rect(LLView* parent, LLRect *rect) +{ + if (!parent) return false; + + LLView::child_list_t::const_iterator itor = + parent->getChildList()->begin(); + for (;itor != parent->getChildList()->end(); ++itor) + { + LLView *last_view = (*itor); + if (last_view->getFromXUI()) + { + *rect = last_view->getRect(); + return true; + } + } + return false; +} + +//static +void LLView::applyXUILayout(LLView::Params& p, LLView* parent, LLRect layout_rect) +{ + if (!parent) return; + + const S32 VPAD = 4; + const S32 MIN_WIDGET_HEIGHT = 10; + + // *NOTE: This will confuse export of floater/panel coordinates unless + // the default is also "topleft". JC + if (p.layout().empty()) + { + p.layout = parent->getLayout(); + } + + if (layout_rect.isEmpty()) + { + layout_rect = parent->getLocalRect(); + } + + // overwrite uninitialized rect params, using context + LLRect default_rect = parent->getLocalRect(); + + bool layout_topleft = (p.layout() == "topleft"); + + // convert negative or centered coordinates to parent relative values + // Note: some of this logic matches the logic in TypedParam::setValueFromBlock() + if (p.rect.left.isProvided()) + { + p.rect.left = p.rect.left + ((p.rect.left >= 0) ? layout_rect.mLeft : layout_rect.mRight); + } + if (p.rect.right.isProvided()) + { + p.rect.right = p.rect.right + ((p.rect.right >= 0) ? layout_rect.mLeft : layout_rect.mRight); + } + if (p.rect.bottom.isProvided()) + { + p.rect.bottom = p.rect.bottom + ((p.rect.bottom >= 0) ? layout_rect.mBottom : layout_rect.mTop); + if (layout_topleft) + { + //invert top to bottom + p.rect.bottom = layout_rect.mBottom + layout_rect.mTop - p.rect.bottom; + } + } + if (p.rect.top.isProvided()) + { + p.rect.top = p.rect.top + ((p.rect.top >= 0) ? layout_rect.mBottom : layout_rect.mTop); + if (layout_topleft) + { + //invert top to bottom + p.rect.top = layout_rect.mBottom + layout_rect.mTop - p.rect.top; + } + } + + // DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels + if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0) + { + p.rect.height = MIN_WIDGET_HEIGHT; + } + + default_rect.translate(0, default_rect.getHeight()); + + // If there was a recently constructed child, use its rectangle + get_last_child_rect(parent, &default_rect); + + if (layout_topleft) + { + // Invert the sense of bottom_delta for topleft layout + if (p.bottom_delta.isProvided()) + { + p.bottom_delta = -p.bottom_delta; + } + else if (p.top_pad.isProvided()) + { + p.bottom_delta = -(p.rect.height + p.top_pad); + } + else if (p.top_delta.isProvided()) + { + p.bottom_delta = + -(p.top_delta + p.rect.height - default_rect.getHeight()); + } + else if (!p.left_delta.isProvided() + && !p.left_pad.isProvided()) + { + // set default position is just below last rect + p.bottom_delta.set(-(p.rect.height + VPAD), false); + } + else + { + p.bottom_delta.set(0, false); + } + + // default to same left edge + if (!p.left_delta.isProvided()) + { + p.left_delta.set(0, false); + } + if (p.left_pad.isProvided()) + { + // left_pad is based on prior widget's right edge + p.left_delta.set(p.left_pad + default_rect.getWidth(), false); + } + + default_rect.translate(p.left_delta, p.bottom_delta); + } + else + { + // set default position is just below last rect + if (!p.bottom_delta.isProvided()) + { + p.bottom_delta.set(-(p.rect.height + VPAD), false); + } + if (!p.left_delta.isProvided()) + { + p.left_delta.set(0, false); + } + default_rect.translate(p.left_delta, p.bottom_delta); + } + + // this handles case where *both* x and x_delta are provided + // ignore x in favor of default x + x_delta + if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false); + if (p.left_delta.isProvided()) p.rect.left.set(0, false); + + // selectively apply rectangle defaults, making sure that + // params are not flagged as having been "provided" + // as rect params are overconstrained and rely on provided flags + if (!p.rect.left.isProvided()) + { + p.rect.left.set(default_rect.mLeft, false); + //HACK: get around the fact that setting a rect param component value won't invalidate the existing rect object value + p.rect.paramChanged(p.rect.left, true); + } + if (!p.rect.bottom.isProvided()) + { + p.rect.bottom.set(default_rect.mBottom, false); + p.rect.paramChanged(p.rect.bottom, true); + } + if (!p.rect.top.isProvided()) + { + p.rect.top.set(default_rect.mTop, false); + p.rect.paramChanged(p.rect.top, true); + } + if (!p.rect.right.isProvided()) + { + p.rect.right.set(default_rect.mRight, false); + p.rect.paramChanged(p.rect.right, true); + + } + if (!p.rect.width.isProvided()) + { + p.rect.width.set(default_rect.getWidth(), false); + p.rect.paramChanged(p.rect.width, true); + } + if (!p.rect.height.isProvided()) + { + p.rect.height.set(default_rect.getHeight(), false); + p.rect.paramChanged(p.rect.height, true); + } +} + +static S32 invert_vertical(S32 y, LLView* parent) +{ + if (y < 0) + { + // already based on top-left, just invert + return -y; + } + else if (parent) + { + // use parent to flip coordinate + S32 parent_height = parent->getRect().getHeight(); + return parent_height - y; + } + else + { + LL_WARNS() << "Attempting to convert layout to top-left with no parent" << LL_ENDL; + return y; + } +} + +// Assumes that input is in bottom-left coordinates, hence must call +// _before_ convert_coords_to_top_left(). +static void convert_to_relative_layout(LLView::Params& p, LLView* parent) +{ + // Use setupParams to get the final widget rectangle + // according to our wacky layout rules. + LLView::Params final = p; + LLView::applyXUILayout(final, parent); + // Must actually extract the rectangle to get consistent + // right = left+width, top = bottom+height + LLRect final_rect = final.rect; + + // We prefer to write out top edge instead of bottom, regardless + // of whether we use relative positioning + bool converted_top = false; + + // Look for a last rectangle + LLRect last_rect; + if (get_last_child_rect(parent, &last_rect)) + { + // ...we have a previous widget to compare to + const S32 EDGE_THRESHOLD_PIXELS = 4; + S32 left_pad = final_rect.mLeft - last_rect.mRight; + S32 left_delta = final_rect.mLeft - last_rect.mLeft; + S32 top_pad = final_rect.mTop - last_rect.mBottom; + S32 top_delta = final_rect.mTop - last_rect.mTop; + // If my left edge is almost the same, or my top edge is + // almost the same... + if (llabs(left_delta) <= EDGE_THRESHOLD_PIXELS + || llabs(top_delta) <= EDGE_THRESHOLD_PIXELS) + { + // ...use relative positioning + // prefer top_pad if widgets are stacking vertically + // (coordinate system is still bottom-left here) + if (top_pad < 0) + { + p.top_pad = top_pad; + p.top_delta.setProvided(false); + } + else + { + p.top_pad.setProvided(false); + p.top_delta = top_delta; + } + // null out other vertical specifiers + p.rect.top.setProvided(false); + p.rect.bottom.setProvided(false); + p.bottom_delta.setProvided(false); + converted_top = true; + + // prefer left_pad if widgets are stacking horizontally + if (left_pad > 0) + { + p.left_pad = left_pad; + p.left_delta.setProvided(false); + } + else + { + p.left_pad.setProvided(false); + p.left_delta = left_delta; + } + p.rect.left.setProvided(false); + p.rect.right.setProvided(false); + } + } + + if (!converted_top) + { + // ...this is the first widget, or one that wasn't aligned + // prefer top/left specification + p.rect.top = final_rect.mTop; + p.rect.bottom.setProvided(false); + p.bottom_delta.setProvided(false); + p.top_pad.setProvided(false); + p.top_delta.setProvided(false); + } +} + +static void convert_coords_to_top_left(LLView::Params& p, LLView* parent) +{ + // Convert the coordinate system to be top-left based. + if (p.rect.top.isProvided()) + { + p.rect.top = invert_vertical(p.rect.top, parent); + } + if (p.rect.bottom.isProvided()) + { + p.rect.bottom = invert_vertical(p.rect.bottom, parent); + } + if (p.top_pad.isProvided()) + { + p.top_pad = -p.top_pad; + } + if (p.top_delta.isProvided()) + { + p.top_delta = -p.top_delta; + } + if (p.bottom_delta.isProvided()) + { + p.bottom_delta = -p.bottom_delta; + } + p.layout = "topleft"; +} + +//static +void LLView::setupParamsForExport(Params& p, LLView* parent) +{ + // Don't convert if already top-left based + if (p.layout() == "topleft") + { + return; + } + + // heuristic: Many of our floaters and panels were bulk-exported. + // These specify exactly bottom/left and height/width. + // Others were done by hand using bottom_delta and/or left_delta. + // Some rely on not specifying left to mean align with left edge. + // Try to convert both to use relative layout, but using top-left + // coordinates. + // Avoid rectangles where top/bottom/left/right was specified. + if (p.rect.height.isProvided() && p.rect.width.isProvided()) + { + if (p.rect.bottom.isProvided() && p.rect.left.isProvided()) + { + // standard bulk export, convert it + convert_to_relative_layout(p, parent); + } + else if (p.rect.bottom.isProvided() && p.left_delta.isProvided()) + { + // hand layout with left_delta + convert_to_relative_layout(p, parent); + } + else if (p.bottom_delta.isProvided()) + { + // hand layout with bottom_delta + // don't check for p.rect.left or p.left_delta because sometimes + // this layout doesn't set it for widgets that are left-aligned + convert_to_relative_layout(p, parent); + } + } + + convert_coords_to_top_left(p, parent); +} + +LLView::tree_iterator_t LLView::beginTreeDFS() +{ + return tree_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); +} + +LLView::tree_iterator_t LLView::endTreeDFS() +{ + // an empty iterator is an "end" iterator + return tree_iterator_t(); +} + +LLView::tree_post_iterator_t LLView::beginTreeDFSPost() +{ + return tree_post_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); +} + +LLView::tree_post_iterator_t LLView::endTreeDFSPost() +{ + // an empty iterator is an "end" iterator + return tree_post_iterator_t(); +} + +LLView::bfs_tree_iterator_t LLView::beginTreeBFS() +{ + return bfs_tree_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); +} + +LLView::bfs_tree_iterator_t LLView::endTreeBFS() +{ + // an empty iterator is an "end" iterator + return bfs_tree_iterator_t(); +} + + +LLView::root_to_view_iterator_t LLView::beginRootToView() +{ + return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1)); +} + +LLView::root_to_view_iterator_t LLView::endRootToView() +{ + return root_to_view_iterator_t(); +} + + +// only create maps on demand, as they incur heap allocation/deallocation cost +// when a view is constructed/deconstructed +LLView& LLView::getDefaultWidgetContainer() const +{ + if (!mDefaultWidgets) + { + LLView::Params p; + p.name = "default widget container"; + p.visible = false; // ensures default widgets can't steal focus, etc. + mDefaultWidgets = new LLView(p); + } + return *mDefaultWidgets; +} + +S32 LLView::notifyParent(const LLSD& info) +{ + LLView* parent = getParent(); + if(parent) + return parent->notifyParent(info); + return 0; +} +bool LLView::notifyChildren(const LLSD& info) +{ + bool ret = false; + for (LLView* childp : mChildList) + { + ret = ret || childp->notifyChildren(info); + } + return ret; +} + +// convenient accessor for draw context +const LLViewDrawContext& LLView::getDrawContext() +{ + return LLViewDrawContext::getCurrentContext(); +} + +const LLViewDrawContext& LLViewDrawContext::getCurrentContext() +{ + static LLViewDrawContext default_context; + + if (sDrawContextStack.empty()) + return default_context; + + return *sDrawContextStack.back(); +} + +LLSD LLView::getInfo(void) +{ + LLSD info; + addInfo(info); + return info; +} + +void LLView::addInfo(LLSD & info) +{ + info["path"] = getPathname(); + info["class"] = typeid(*this).name(); + info["visible"] = getVisible(); + info["visible_chain"] = isInVisibleChain(); + info["enabled"] = getEnabled(); + info["enabled_chain"] = isInEnabledChain(); + info["available"] = isAvailable(); + LLRect rect(calcScreenRect()); + info["rect"] = LLSDMap("left", rect.mLeft)("top", rect.mTop) + ("right", rect.mRight)("bottom", rect.mBottom); +} diff --git a/indra/llui/llview.h b/indra/llui/llview.h index ddb0a3dbbf..3af748dda6 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -1,737 +1,737 @@ -/** - * @file llview.h - * @brief Container for other views, anything that draws. - * - * $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$ - */ - -#ifndef LL_LLVIEW_H -#define LL_LLVIEW_H - -// A view is an area in a window that can draw. It might represent -// the HUD or a dialog box or a button. It can also contain sub-views -// and child widgets - -#include "stdtypes.h" -#include "llcoord.h" -#include "llfontgl.h" -#include "llhandle.h" -#include "llmortician.h" -#include "llmousehandler.h" -#include "llstring.h" -#include "llrect.h" -#include "llui.h" -#include "lluistring.h" -#include "llviewquery.h" -#include "lluistring.h" -#include "llcursortypes.h" -#include "lluictrlfactory.h" -#include "lltreeiterators.h" -#include "llfocusmgr.h" - -#include -#include -#include - -class LLSD; - -const U32 FOLLOWS_NONE = 0x00; -const U32 FOLLOWS_LEFT = 0x01; -const U32 FOLLOWS_RIGHT = 0x02; -const U32 FOLLOWS_TOP = 0x10; -const U32 FOLLOWS_BOTTOM = 0x20; -const U32 FOLLOWS_ALL = 0x33; - -const bool MOUSE_OPAQUE = true; -const bool NOT_MOUSE_OPAQUE = false; - -const U32 GL_NAME_UI_RESERVED = 2; - - -// maintains render state during traversal of UI tree -class LLViewDrawContext -{ -public: - F32 mAlpha; - - LLViewDrawContext(F32 alpha = 1.f) - : mAlpha(alpha) - { - if (!sDrawContextStack.empty()) - { - LLViewDrawContext* context_top = sDrawContextStack.back(); - // merge with top of stack - mAlpha *= context_top->mAlpha; - } - sDrawContextStack.push_back(this); - } - - ~LLViewDrawContext() - { - sDrawContextStack.pop_back(); - } - - static const LLViewDrawContext& getCurrentContext(); - -private: - static std::vector sDrawContextStack; -}; - -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: - - enum EOrientation { HORIZONTAL, VERTICAL, ORIENTATION_COUNT }; - - 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(); - }; - - // most widgets are valid children of LLView - typedef LLDefaultChildRegistry child_registry_t; - - void initFromParams(const LLView::Params&); - -protected: - LLView(const LLView::Params&); - friend class LLUICtrlFactory; - -private: - // widgets in general are not copyable - LLView(const LLView& other); -public: -//#if LL_DEBUG - static bool sIsDrawing; -//#endif - enum ESoundFlags - { - SILENT = 0, - MOUSE_DOWN = 1, - MOUSE_UP = 2 - }; - - enum ESnapType - { - SNAP_PARENT, - SNAP_SIBLINGS, - SNAP_PARENT_AND_SIBLINGS - }; - - enum ESnapEdge - { - SNAP_LEFT, - SNAP_TOP, - SNAP_RIGHT, - SNAP_BOTTOM - }; - - typedef std::list child_list_t; - typedef child_list_t::iterator child_list_iter_t; - typedef child_list_t::const_iterator child_list_const_iter_t; - typedef child_list_t::reverse_iterator child_list_reverse_iter_t; - typedef child_list_t::const_reverse_iterator child_list_const_reverse_iter_t; - - typedef std::pair tab_order_pair_t; - // this structure primarily sorts by the tab group, secondarily by the insertion ordinal (lastly by the value of the pointer) - typedef std::map child_tab_order_t; - typedef child_tab_order_t::iterator child_tab_order_iter_t; - typedef child_tab_order_t::const_iterator child_tab_order_const_iter_t; - typedef child_tab_order_t::reverse_iterator child_tab_order_reverse_iter_t; - typedef child_tab_order_t::const_reverse_iterator child_tab_order_const_reverse_iter_t; - - virtual ~LLView(); - - // Some UI widgets need to be added as controls. Others need to - // be added as regular view children. isCtrl should return true - // if a widget needs to be added as a ctrl - virtual bool isCtrl() const; - - virtual bool isPanel() const; - - // - // MANIPULATORS - // - void setMouseOpaque( bool b ) { mMouseOpaque = b; } - bool getMouseOpaque() const { return mMouseOpaque; } - void setToolTip( const LLStringExplicit& msg ); - bool setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text ); - void setToolTipArgs( const LLStringUtil::format_map_t& args ); - - virtual void setRect(const LLRect &rect); - void setFollows(U32 flags) { mReshapeFlags = flags; } - - // deprecated, use setFollows() with FOLLOWS_LEFT | FOLLOWS_TOP, etc. - void setFollowsNone() { mReshapeFlags = FOLLOWS_NONE; } - void setFollowsLeft() { mReshapeFlags |= FOLLOWS_LEFT; } - void setFollowsTop() { mReshapeFlags |= FOLLOWS_TOP; } - void setFollowsRight() { mReshapeFlags |= FOLLOWS_RIGHT; } - void setFollowsBottom() { mReshapeFlags |= FOLLOWS_BOTTOM; } - void setFollowsAll() { mReshapeFlags |= FOLLOWS_ALL; } - - void setSoundFlags(U8 flags) { mSoundFlags = flags; } - void setName(std::string name) { mName = name; } - void setUseBoundingRect( bool use_bounding_rect ); - bool getUseBoundingRect() const; - - ECursorType getHoverCursor() { return mHoverCursor; } - - static F32 getTooltipTimeout(); - virtual const std::string getToolTip() const; - virtual const std::string& getText() const { return LLStringUtil::null; } - virtual const LLFontGL* getFont() const { return nullptr; } - - void sendChildToFront(LLView* child); - void sendChildToBack(LLView* child); - - virtual bool addChild(LLView* view, S32 tab_group = 0); - - // implemented in terms of addChild() - bool addChildInBack(LLView* view, S32 tab_group = 0); - - // remove the specified child from the view, and set it's parent to NULL. - virtual void removeChild(LLView* view); - - virtual bool postBuild() { return true; } - - const child_tab_order_t& getTabOrder() const { return mTabOrder; } - - void setDefaultTabGroup(S32 d) { mDefaultTabGroup = d; } - S32 getDefaultTabGroup() const { return mDefaultTabGroup; } - S32 getLastTabGroup() { return mLastTabGroup; } - - bool isInVisibleChain() const; - bool isInEnabledChain() const; - - void setFocusRoot(bool b) { mIsFocusRoot = b; } - bool isFocusRoot() const { return mIsFocusRoot; } - virtual bool canFocusChildren() const; - - bool focusNextRoot(); - bool focusPrevRoot(); - - // Normally we want the app menus to get priority on accelerated keys - // However, sometimes we want to give specific views a first chance - // iat handling them. (eg. the script editor) - virtual bool hasAccelerators() const { return false; }; - - // delete all children. Override this function if you need to - // perform any extra clean up such as cached pointers to selected - // children, etc. - virtual void deleteAllChildren(); - - void setAllChildrenEnabled(bool b); - - virtual void setVisible(bool visible); - void setVisibleDirect(bool visible) { mVisible = visible; } - const bool& getVisible() const { return mVisible; } - virtual void setEnabled(bool enabled); - bool getEnabled() const { return mEnabled; } - /// 'available' in this context means 'visible and enabled': in other - /// words, can a user actually interact with this? - virtual bool isAvailable() const; - /// The static isAvailable() tests an LLView* that could be NULL. - static bool isAvailable(const LLView* view); - U8 getSoundFlags() const { return mSoundFlags; } - - virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); - - virtual void onVisibilityChange ( bool new_visibility ); - virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); - - void pushVisible(bool visible) { mLastVisible = mVisible; setVisible(visible); } - void popVisible() { setVisible(mLastVisible); } - bool getLastVisible() const { return mLastVisible; } - - U32 getFollows() const { return mReshapeFlags; } - bool followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; } - bool followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; } - bool followsTop() const { return mReshapeFlags & FOLLOWS_TOP; } - bool followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; } - bool followsAll() const { return mReshapeFlags & FOLLOWS_ALL; } - - const LLRect& getRect() const { return mRect; } - const LLRect& getBoundingRect() const { return mBoundingRect; } - LLRect getLocalBoundingRect() const; - LLRect calcScreenRect() const; - LLRect calcScreenBoundingRect() const; - LLRect getLocalRect() const; - virtual LLRect getSnapRect() const; - LLRect getLocalSnapRect() const; - - std::string getLayout() { return mLayout; } - - // 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; - bool hasChild(const std::string& childname, bool recurse = false) const; - bool childHasKeyboardFocus( const std::string& childname ) const; - - // these iterators are used for collapsing various tree traversals into for loops - typedef LLTreeDFSIter tree_iterator_t; - tree_iterator_t beginTreeDFS(); - tree_iterator_t endTreeDFS(); - - typedef LLTreeDFSPostIter tree_post_iterator_t; - tree_post_iterator_t beginTreeDFSPost(); - tree_post_iterator_t endTreeDFSPost(); - - typedef LLTreeBFSIter bfs_tree_iterator_t; - bfs_tree_iterator_t beginTreeBFS(); - bfs_tree_iterator_t endTreeBFS(); - - - typedef LLTreeDownIter root_to_view_iterator_t; - root_to_view_iterator_t beginRootToView(); - root_to_view_iterator_t endRootToView(); - - // - // UTILITIES - // - - // Default behavior is to use reshape flags to resize child views - virtual void reshape(S32 width, S32 height, bool called_from_parent = true); - 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, S32 min_overlap_pixels = S32_MAX); - bool translateRectIntoRect( const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels = S32_MAX); - bool translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels = S32_MAX); - void centerWithin(const LLRect& bounds); - - void setShape(const LLRect& new_rect, bool by_user = false); - 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); - virtual bool canSnapTo(const LLView* other_view); - virtual void setSnappedTo(const LLView* snap_view); - - // inherited from LLFocusableElement - /* virtual */ bool handleKey(KEY key, MASK mask, bool called_from_parent); - /* virtual */ bool handleKeyUp(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(); - - void parseFollowsFlags(const LLView::Params& params); - - // Some widgets, like close box buttons, don't need to be saved - bool getFromXUI() const { return mFromXUI; } - void setFromXUI(bool b) { mFromXUI = b; } - - typedef enum e_hit_test_type - { - HIT_TEST_USE_BOUNDING_RECT, - HIT_TEST_IGNORE_BOUNDING_RECT - }EHitTestType; - - bool parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; - bool pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; - bool blockMouseEvent(S32 x, S32 y) const; - - // See LLMouseHandler virtuals for screenPointToLocal and localPointToScreen - bool localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const; - bool localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const; - void screenRectToLocal( const LLRect& screen, LLRect* local ) const; - void localRectToScreen( const LLRect& local, LLRect* screen ) const; - - LLControlVariable *findControl(const std::string& name); - - 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 - /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); - - /*virtual*/ const std::string& getName() const; - /*virtual*/ void onMouseCaptureLost(); - /*virtual*/ bool hasMouseCapture(); - /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; - /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; - - virtual LLView* childFromPoint(S32 x, S32 y, bool recur=false); - - // view-specific handlers - virtual void onMouseEnter(S32 x, S32 y, MASK mask); - virtual void onMouseLeave(S32 x, S32 y, MASK mask); - - std::string getPathname() const; - // static method handles NULL pointer too - static std::string getPathname(const LLView*); - - template T* findChild(const std::string& name, bool recurse = true) const - { - LLView* child = findChildView(name, recurse); - T* result = dynamic_cast(child); - return result; - } - - template T* getChild(const std::string& name, bool recurse = true) const; - - template T& getChildRef(const std::string& name, bool recurse = true) const - { - return *getChild(name, recurse); - } - - virtual LLView* getChildView(const std::string& name, bool recurse = true) const; - virtual LLView* findChildView(const std::string& name, bool recurse = true) const; - - template T* getDefaultWidget(const std::string& name) const - { - LLView* widgetp = getDefaultWidgetContainer().findChildView(name); - return dynamic_cast(widgetp); - } - - template T* getParentByType() const - { - LLView* parent = getParent(); - while(parent) - { - if (dynamic_cast(parent)) - { - return static_cast(parent); - } - parent = parent->getParent(); - } - return NULL; - } - - ////////////////////////////////////////////// - // statics - ////////////////////////////////////////////// - //static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node); - - // focuses the item in the list after the currently-focused item, wrapping if necessary - static bool focusNext(LLView::child_list_t & result); - // focuses the item in the list before the currently-focused item, wrapping if necessary - static bool focusPrev(LLView::child_list_t & result); - - // returns query for iterating over controls in tab order - static const LLViewQuery & getTabOrderQuery(); - // return query for iterating over focus roots in tab order - static const LLViewQuery & getFocusRootsQuery(); - - static LLWindow* getWindow(void) { return LLUI::getInstance()->mWindow; } - - // Set up params after XML load before calling new(), - // usually to adjust layout. - static void applyXUILayout(Params& p, LLView* parent, LLRect layout_rect = LLRect()); - - // For re-export of floaters and panels, convert the coordinate system - // to be top-left based. - static void setupParamsForExport(Params& p, LLView* parent); - - //virtual bool addChildFromParam(const LLInitParam::BaseBlock& params) { return true; } - virtual bool handleKeyHere(KEY key, MASK mask); - virtual bool handleKeyUpHere(KEY key, MASK mask); - virtual bool handleUnicodeCharHere(llwchar uni_char); - - virtual void handleReshape(const LLRect& rect, bool by_user); - virtual void dirtyRect(); - - //send custom notification to LLView parent - virtual S32 notifyParent(const LLSD& info); - - //send custom notification to all view childrend - // return true if _any_ children return true. otherwise false. - virtual bool notifyChildren(const LLSD& info); - - //send custom notification to current view - virtual S32 notify(const LLSD& info) { return 0;}; - - static const LLViewDrawContext& getDrawContext(); - - // Returns useful information about this ui widget. - LLSD getInfo(void); - -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* childrenHandleKeyUp(KEY key, MASK mask); - LLView* childrenHandleUnicodeChar(llwchar uni_char); - LLView* childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, - bool drop, - EDragAndDropType type, - void* data, - EAcceptance* accept, - std::string& tooltip_msg); - - LLView* childrenHandleHover(S32 x, S32 y, MASK mask); - LLView* childrenHandleMouseUp(S32 x, S32 y, MASK mask); - LLView* childrenHandleMouseDown(S32 x, S32 y, MASK mask); - LLView* childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask); - LLView* childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask); - LLView* childrenHandleDoubleClick(S32 x, S32 y, MASK mask); - LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks); - LLView* childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks); - LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask); - LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask); - LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask); - - ECursorType mHoverCursor; - - virtual void addInfo(LLSD & info); -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; - - // location in pixels, relative to surrounding structure, bottom,left=0,0 - bool mVisible; - LLRect mRect; - LLRect mBoundingRect; - - std::string mLayout; - std::string mName; - - U32 mReshapeFlags; - - child_tab_order_t mTabOrder; - 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. - bool mMouseOpaque; // Opaque views handle all mouse events that are over their rect. - LLUIString mToolTipMsg; // isNull() is true if none. - - U8 mSoundFlags; - bool mFromXUI; - - bool mIsFocusRoot; - bool mUseBoundingRect; // hit test against bounding rectangle that includes all child elements - - bool mLastVisible; - - bool mInDraw; - - static LLWindow* sWindow; // All root views must know about their window. - - typedef std::map default_widget_map_t; - // allocate this map no demand, as it is rarely needed - mutable LLView* mDefaultWidgets; - - LLView& getDefaultWidgetContainer() const; - - // 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; - }; - - // Depth in view hierarchy during rendering - static S32 sDepth; - - // Draw debug rectangles around widgets to help with alignment and spacing - static bool sDebugRects; - - // Show hexadecimal byte values of unicode symbols in a tooltip - static bool sDebugUnicode; - - // Show camera position and direction in Camera Controls floater - static bool sDebugCamera; - - static bool sIsRectDirty; - static LLRect sDirtyRect; - - // Draw widget names and sizes when drawing debug rectangles, turning this - // off is useful to make the rectangles themselves easier to see. - static bool sDebugRectsShowNames; - - static bool sDebugKeys; - static bool sDebugMouseHandling; - static std::string sMouseHandlerMessage; - static S32 sSelectID; - static std::set sPreviewHighlightedElements; // DEV-16869 - static bool sHighlightingDiffs; // DEV-16869 - static LLView* sPreviewClickedElement; // DEV-16869 - static bool sDrawPreviewHighlights; - static S32 sLastLeftXML; - static S32 sLastBottomXML; - static bool sForceReshape; -}; - -namespace LLInitParam -{ -template<> -struct TypeValues : public LLInitParam::TypeValuesHelper -{ - static void declareValues(); -}; -} - -template T* LLView::getChild(const std::string& name, bool recurse) const -{ - LLView* child = findChildView(name, recurse); - T* result = dynamic_cast(child); - if (!result) - { - // did we find *something* with that name? - if (child) - { - LL_WARNS() << "Found child named \"" << name << "\" but of wrong type " << typeid(*child).name() << ", expecting " << typeid(T*).name() << LL_ENDL; - } - result = getDefaultWidget(name); - if (!result) - { - result = LLUICtrlFactory::getDefaultWidget(name); - if (!result) - { - LL_ERRS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL; - } - - // *NOTE: You cannot call mFoo = getChild("bar") - // in a floater or panel constructor. The widgets will not - // be ready. Instead, put it in postBuild(). - LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL; - - getDefaultWidgetContainer().addChild(result); - } - } - return result; -} - -// Compiler optimization - don't generate these specializations inline, -// require explicit specialization. See llbutton.cpp for an example. -#ifndef LLVIEW_CPP -extern template class LLView* LLView::getChild( - const std::string& name, bool recurse) const; -#endif - -#endif //LL_LLVIEW_H +/** + * @file llview.h + * @brief Container for other views, anything that draws. + * + * $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$ + */ + +#ifndef LL_LLVIEW_H +#define LL_LLVIEW_H + +// A view is an area in a window that can draw. It might represent +// the HUD or a dialog box or a button. It can also contain sub-views +// and child widgets + +#include "stdtypes.h" +#include "llcoord.h" +#include "llfontgl.h" +#include "llhandle.h" +#include "llmortician.h" +#include "llmousehandler.h" +#include "llstring.h" +#include "llrect.h" +#include "llui.h" +#include "lluistring.h" +#include "llviewquery.h" +#include "lluistring.h" +#include "llcursortypes.h" +#include "lluictrlfactory.h" +#include "lltreeiterators.h" +#include "llfocusmgr.h" + +#include +#include +#include + +class LLSD; + +const U32 FOLLOWS_NONE = 0x00; +const U32 FOLLOWS_LEFT = 0x01; +const U32 FOLLOWS_RIGHT = 0x02; +const U32 FOLLOWS_TOP = 0x10; +const U32 FOLLOWS_BOTTOM = 0x20; +const U32 FOLLOWS_ALL = 0x33; + +const bool MOUSE_OPAQUE = true; +const bool NOT_MOUSE_OPAQUE = false; + +const U32 GL_NAME_UI_RESERVED = 2; + + +// maintains render state during traversal of UI tree +class LLViewDrawContext +{ +public: + F32 mAlpha; + + LLViewDrawContext(F32 alpha = 1.f) + : mAlpha(alpha) + { + if (!sDrawContextStack.empty()) + { + LLViewDrawContext* context_top = sDrawContextStack.back(); + // merge with top of stack + mAlpha *= context_top->mAlpha; + } + sDrawContextStack.push_back(this); + } + + ~LLViewDrawContext() + { + sDrawContextStack.pop_back(); + } + + static const LLViewDrawContext& getCurrentContext(); + +private: + static std::vector sDrawContextStack; +}; + +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: + + enum EOrientation { HORIZONTAL, VERTICAL, ORIENTATION_COUNT }; + + 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(); + }; + + // most widgets are valid children of LLView + typedef LLDefaultChildRegistry child_registry_t; + + void initFromParams(const LLView::Params&); + +protected: + LLView(const LLView::Params&); + friend class LLUICtrlFactory; + +private: + // widgets in general are not copyable + LLView(const LLView& other); +public: +//#if LL_DEBUG + static bool sIsDrawing; +//#endif + enum ESoundFlags + { + SILENT = 0, + MOUSE_DOWN = 1, + MOUSE_UP = 2 + }; + + enum ESnapType + { + SNAP_PARENT, + SNAP_SIBLINGS, + SNAP_PARENT_AND_SIBLINGS + }; + + enum ESnapEdge + { + SNAP_LEFT, + SNAP_TOP, + SNAP_RIGHT, + SNAP_BOTTOM + }; + + typedef std::list child_list_t; + typedef child_list_t::iterator child_list_iter_t; + typedef child_list_t::const_iterator child_list_const_iter_t; + typedef child_list_t::reverse_iterator child_list_reverse_iter_t; + typedef child_list_t::const_reverse_iterator child_list_const_reverse_iter_t; + + typedef std::pair tab_order_pair_t; + // this structure primarily sorts by the tab group, secondarily by the insertion ordinal (lastly by the value of the pointer) + typedef std::map child_tab_order_t; + typedef child_tab_order_t::iterator child_tab_order_iter_t; + typedef child_tab_order_t::const_iterator child_tab_order_const_iter_t; + typedef child_tab_order_t::reverse_iterator child_tab_order_reverse_iter_t; + typedef child_tab_order_t::const_reverse_iterator child_tab_order_const_reverse_iter_t; + + virtual ~LLView(); + + // Some UI widgets need to be added as controls. Others need to + // be added as regular view children. isCtrl should return true + // if a widget needs to be added as a ctrl + virtual bool isCtrl() const; + + virtual bool isPanel() const; + + // + // MANIPULATORS + // + void setMouseOpaque( bool b ) { mMouseOpaque = b; } + bool getMouseOpaque() const { return mMouseOpaque; } + void setToolTip( const LLStringExplicit& msg ); + bool setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text ); + void setToolTipArgs( const LLStringUtil::format_map_t& args ); + + virtual void setRect(const LLRect &rect); + void setFollows(U32 flags) { mReshapeFlags = flags; } + + // deprecated, use setFollows() with FOLLOWS_LEFT | FOLLOWS_TOP, etc. + void setFollowsNone() { mReshapeFlags = FOLLOWS_NONE; } + void setFollowsLeft() { mReshapeFlags |= FOLLOWS_LEFT; } + void setFollowsTop() { mReshapeFlags |= FOLLOWS_TOP; } + void setFollowsRight() { mReshapeFlags |= FOLLOWS_RIGHT; } + void setFollowsBottom() { mReshapeFlags |= FOLLOWS_BOTTOM; } + void setFollowsAll() { mReshapeFlags |= FOLLOWS_ALL; } + + void setSoundFlags(U8 flags) { mSoundFlags = flags; } + void setName(std::string name) { mName = name; } + void setUseBoundingRect( bool use_bounding_rect ); + bool getUseBoundingRect() const; + + ECursorType getHoverCursor() { return mHoverCursor; } + + static F32 getTooltipTimeout(); + virtual const std::string getToolTip() const; + virtual const std::string& getText() const { return LLStringUtil::null; } + virtual const LLFontGL* getFont() const { return nullptr; } + + void sendChildToFront(LLView* child); + void sendChildToBack(LLView* child); + + virtual bool addChild(LLView* view, S32 tab_group = 0); + + // implemented in terms of addChild() + bool addChildInBack(LLView* view, S32 tab_group = 0); + + // remove the specified child from the view, and set it's parent to NULL. + virtual void removeChild(LLView* view); + + virtual bool postBuild() { return true; } + + const child_tab_order_t& getTabOrder() const { return mTabOrder; } + + void setDefaultTabGroup(S32 d) { mDefaultTabGroup = d; } + S32 getDefaultTabGroup() const { return mDefaultTabGroup; } + S32 getLastTabGroup() { return mLastTabGroup; } + + bool isInVisibleChain() const; + bool isInEnabledChain() const; + + void setFocusRoot(bool b) { mIsFocusRoot = b; } + bool isFocusRoot() const { return mIsFocusRoot; } + virtual bool canFocusChildren() const; + + bool focusNextRoot(); + bool focusPrevRoot(); + + // Normally we want the app menus to get priority on accelerated keys + // However, sometimes we want to give specific views a first chance + // iat handling them. (eg. the script editor) + virtual bool hasAccelerators() const { return false; }; + + // delete all children. Override this function if you need to + // perform any extra clean up such as cached pointers to selected + // children, etc. + virtual void deleteAllChildren(); + + void setAllChildrenEnabled(bool b); + + virtual void setVisible(bool visible); + void setVisibleDirect(bool visible) { mVisible = visible; } + const bool& getVisible() const { return mVisible; } + virtual void setEnabled(bool enabled); + bool getEnabled() const { return mEnabled; } + /// 'available' in this context means 'visible and enabled': in other + /// words, can a user actually interact with this? + virtual bool isAvailable() const; + /// The static isAvailable() tests an LLView* that could be NULL. + static bool isAvailable(const LLView* view); + U8 getSoundFlags() const { return mSoundFlags; } + + virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text ); + + virtual void onVisibilityChange ( bool new_visibility ); + virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); + + void pushVisible(bool visible) { mLastVisible = mVisible; setVisible(visible); } + void popVisible() { setVisible(mLastVisible); } + bool getLastVisible() const { return mLastVisible; } + + U32 getFollows() const { return mReshapeFlags; } + bool followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; } + bool followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; } + bool followsTop() const { return mReshapeFlags & FOLLOWS_TOP; } + bool followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; } + bool followsAll() const { return mReshapeFlags & FOLLOWS_ALL; } + + const LLRect& getRect() const { return mRect; } + const LLRect& getBoundingRect() const { return mBoundingRect; } + LLRect getLocalBoundingRect() const; + LLRect calcScreenRect() const; + LLRect calcScreenBoundingRect() const; + LLRect getLocalRect() const; + virtual LLRect getSnapRect() const; + LLRect getLocalSnapRect() const; + + std::string getLayout() { return mLayout; } + + // 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; + bool hasChild(const std::string& childname, bool recurse = false) const; + bool childHasKeyboardFocus( const std::string& childname ) const; + + // these iterators are used for collapsing various tree traversals into for loops + typedef LLTreeDFSIter tree_iterator_t; + tree_iterator_t beginTreeDFS(); + tree_iterator_t endTreeDFS(); + + typedef LLTreeDFSPostIter tree_post_iterator_t; + tree_post_iterator_t beginTreeDFSPost(); + tree_post_iterator_t endTreeDFSPost(); + + typedef LLTreeBFSIter bfs_tree_iterator_t; + bfs_tree_iterator_t beginTreeBFS(); + bfs_tree_iterator_t endTreeBFS(); + + + typedef LLTreeDownIter root_to_view_iterator_t; + root_to_view_iterator_t beginRootToView(); + root_to_view_iterator_t endRootToView(); + + // + // UTILITIES + // + + // Default behavior is to use reshape flags to resize child views + virtual void reshape(S32 width, S32 height, bool called_from_parent = true); + 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, S32 min_overlap_pixels = S32_MAX); + bool translateRectIntoRect( const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels = S32_MAX); + bool translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels = S32_MAX); + void centerWithin(const LLRect& bounds); + + void setShape(const LLRect& new_rect, bool by_user = false); + 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); + virtual bool canSnapTo(const LLView* other_view); + virtual void setSnappedTo(const LLView* snap_view); + + // inherited from LLFocusableElement + /* virtual */ bool handleKey(KEY key, MASK mask, bool called_from_parent); + /* virtual */ bool handleKeyUp(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(); + + void parseFollowsFlags(const LLView::Params& params); + + // Some widgets, like close box buttons, don't need to be saved + bool getFromXUI() const { return mFromXUI; } + void setFromXUI(bool b) { mFromXUI = b; } + + typedef enum e_hit_test_type + { + HIT_TEST_USE_BOUNDING_RECT, + HIT_TEST_IGNORE_BOUNDING_RECT + }EHitTestType; + + bool parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + bool pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + bool blockMouseEvent(S32 x, S32 y) const; + + // See LLMouseHandler virtuals for screenPointToLocal and localPointToScreen + bool localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const; + bool localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const; + void screenRectToLocal( const LLRect& screen, LLRect* local ) const; + void localRectToScreen( const LLRect& local, LLRect* screen ) const; + + LLControlVariable *findControl(const std::string& name); + + 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 + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask); + + /*virtual*/ const std::string& getName() const; + /*virtual*/ void onMouseCaptureLost(); + /*virtual*/ bool hasMouseCapture(); + /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; + /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; + + virtual LLView* childFromPoint(S32 x, S32 y, bool recur=false); + + // view-specific handlers + virtual void onMouseEnter(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask); + + std::string getPathname() const; + // static method handles NULL pointer too + static std::string getPathname(const LLView*); + + template T* findChild(const std::string& name, bool recurse = true) const + { + LLView* child = findChildView(name, recurse); + T* result = dynamic_cast(child); + return result; + } + + template T* getChild(const std::string& name, bool recurse = true) const; + + template T& getChildRef(const std::string& name, bool recurse = true) const + { + return *getChild(name, recurse); + } + + virtual LLView* getChildView(const std::string& name, bool recurse = true) const; + virtual LLView* findChildView(const std::string& name, bool recurse = true) const; + + template T* getDefaultWidget(const std::string& name) const + { + LLView* widgetp = getDefaultWidgetContainer().findChildView(name); + return dynamic_cast(widgetp); + } + + template T* getParentByType() const + { + LLView* parent = getParent(); + while(parent) + { + if (dynamic_cast(parent)) + { + return static_cast(parent); + } + parent = parent->getParent(); + } + return NULL; + } + + ////////////////////////////////////////////// + // statics + ////////////////////////////////////////////// + //static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node); + + // focuses the item in the list after the currently-focused item, wrapping if necessary + static bool focusNext(LLView::child_list_t & result); + // focuses the item in the list before the currently-focused item, wrapping if necessary + static bool focusPrev(LLView::child_list_t & result); + + // returns query for iterating over controls in tab order + static const LLViewQuery & getTabOrderQuery(); + // return query for iterating over focus roots in tab order + static const LLViewQuery & getFocusRootsQuery(); + + static LLWindow* getWindow(void) { return LLUI::getInstance()->mWindow; } + + // Set up params after XML load before calling new(), + // usually to adjust layout. + static void applyXUILayout(Params& p, LLView* parent, LLRect layout_rect = LLRect()); + + // For re-export of floaters and panels, convert the coordinate system + // to be top-left based. + static void setupParamsForExport(Params& p, LLView* parent); + + //virtual bool addChildFromParam(const LLInitParam::BaseBlock& params) { return true; } + virtual bool handleKeyHere(KEY key, MASK mask); + virtual bool handleKeyUpHere(KEY key, MASK mask); + virtual bool handleUnicodeCharHere(llwchar uni_char); + + virtual void handleReshape(const LLRect& rect, bool by_user); + virtual void dirtyRect(); + + //send custom notification to LLView parent + virtual S32 notifyParent(const LLSD& info); + + //send custom notification to all view childrend + // return true if _any_ children return true. otherwise false. + virtual bool notifyChildren(const LLSD& info); + + //send custom notification to current view + virtual S32 notify(const LLSD& info) { return 0;}; + + static const LLViewDrawContext& getDrawContext(); + + // Returns useful information about this ui widget. + LLSD getInfo(void); + +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* childrenHandleKeyUp(KEY key, MASK mask); + LLView* childrenHandleUnicodeChar(llwchar uni_char); + LLView* childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, + EDragAndDropType type, + void* data, + EAcceptance* accept, + std::string& tooltip_msg); + + LLView* childrenHandleHover(S32 x, S32 y, MASK mask); + LLView* childrenHandleMouseUp(S32 x, S32 y, MASK mask); + LLView* childrenHandleMouseDown(S32 x, S32 y, MASK mask); + LLView* childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask); + LLView* childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask); + LLView* childrenHandleDoubleClick(S32 x, S32 y, MASK mask); + LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks); + LLView* childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks); + LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask); + LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask); + LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask); + + ECursorType mHoverCursor; + + virtual void addInfo(LLSD & info); +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; + + // location in pixels, relative to surrounding structure, bottom,left=0,0 + bool mVisible; + LLRect mRect; + LLRect mBoundingRect; + + std::string mLayout; + std::string mName; + + U32 mReshapeFlags; + + child_tab_order_t mTabOrder; + 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. + bool mMouseOpaque; // Opaque views handle all mouse events that are over their rect. + LLUIString mToolTipMsg; // isNull() is true if none. + + U8 mSoundFlags; + bool mFromXUI; + + bool mIsFocusRoot; + bool mUseBoundingRect; // hit test against bounding rectangle that includes all child elements + + bool mLastVisible; + + bool mInDraw; + + static LLWindow* sWindow; // All root views must know about their window. + + typedef std::map default_widget_map_t; + // allocate this map no demand, as it is rarely needed + mutable LLView* mDefaultWidgets; + + LLView& getDefaultWidgetContainer() const; + + // 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; + }; + + // Depth in view hierarchy during rendering + static S32 sDepth; + + // Draw debug rectangles around widgets to help with alignment and spacing + static bool sDebugRects; + + // Show hexadecimal byte values of unicode symbols in a tooltip + static bool sDebugUnicode; + + // Show camera position and direction in Camera Controls floater + static bool sDebugCamera; + + static bool sIsRectDirty; + static LLRect sDirtyRect; + + // Draw widget names and sizes when drawing debug rectangles, turning this + // off is useful to make the rectangles themselves easier to see. + static bool sDebugRectsShowNames; + + static bool sDebugKeys; + static bool sDebugMouseHandling; + static std::string sMouseHandlerMessage; + static S32 sSelectID; + static std::set sPreviewHighlightedElements; // DEV-16869 + static bool sHighlightingDiffs; // DEV-16869 + static LLView* sPreviewClickedElement; // DEV-16869 + static bool sDrawPreviewHighlights; + static S32 sLastLeftXML; + static S32 sLastBottomXML; + static bool sForceReshape; +}; + +namespace LLInitParam +{ +template<> +struct TypeValues : public LLInitParam::TypeValuesHelper +{ + static void declareValues(); +}; +} + +template T* LLView::getChild(const std::string& name, bool recurse) const +{ + LLView* child = findChildView(name, recurse); + T* result = dynamic_cast(child); + if (!result) + { + // did we find *something* with that name? + if (child) + { + LL_WARNS() << "Found child named \"" << name << "\" but of wrong type " << typeid(*child).name() << ", expecting " << typeid(T*).name() << LL_ENDL; + } + result = getDefaultWidget(name); + if (!result) + { + result = LLUICtrlFactory::getDefaultWidget(name); + if (!result) + { + LL_ERRS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL; + } + + // *NOTE: You cannot call mFoo = getChild("bar") + // in a floater or panel constructor. The widgets will not + // be ready. Instead, put it in postBuild(). + LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL; + + getDefaultWidgetContainer().addChild(result); + } + } + return result; +} + +// Compiler optimization - don't generate these specializations inline, +// require explicit specialization. See llbutton.cpp for an example. +#ifndef LLVIEW_CPP +extern template class LLView* LLView::getChild( + const std::string& name, bool recurse) const; +#endif + +#endif //LL_LLVIEW_H diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp index 241192e21c..c1777c972c 100644 --- a/indra/llui/llviewborder.cpp +++ b/indra/llui/llviewborder.cpp @@ -1,270 +1,270 @@ -/** - * @file llviewborder.cpp - * - * $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 "linden_common.h" -#include "llviewborder.h" -#include "llrender.h" -#include "llfocusmgr.h" -#include "lluictrlfactory.h" -#include "lluiimage.h" - -static LLDefaultChildRegistry::Register r("view_border"); - -void LLViewBorder::BevelValues::declareValues() -{ - declare("in", LLViewBorder::BEVEL_IN); - declare("out", LLViewBorder::BEVEL_OUT); - declare("bright", LLViewBorder::BEVEL_BRIGHT); - declare("none", LLViewBorder::BEVEL_NONE); -} - -void LLViewBorder::StyleValues::declareValues() -{ - declare("line", LLViewBorder::STYLE_LINE); - declare("texture", LLViewBorder::STYLE_TEXTURE); -} - -LLViewBorder::Params::Params() -: bevel_style("bevel_style", BEVEL_OUT), - render_style("border_style", STYLE_LINE), - border_thickness("border_thickness"), - highlight_light_color("highlight_light_color"), - highlight_dark_color("highlight_dark_color"), - shadow_light_color("shadow_light_color"), - shadow_dark_color("shadow_dark_color") -{ - addSynonym(border_thickness, "thickness"); - addSynonym(render_style, "style"); -} - - -LLViewBorder::LLViewBorder(const LLViewBorder::Params& p) -: LLView(p), - mTexture( NULL ), - mHasKeyboardFocus( false ), - mBorderWidth(p.border_thickness), - mHighlightLight(p.highlight_light_color()), - mHighlightDark(p.highlight_dark_color()), - mShadowLight(p.shadow_light_color()), - mShadowDark(p.shadow_dark_color()), - mBevel(p.bevel_style), - mStyle(p.render_style) -{} - -void LLViewBorder::setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light ) -{ - mShadowDark = shadow_dark; - mHighlightLight = highlight_light; -} - -void LLViewBorder::setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark, - const LLColor4& highlight_light, const LLColor4& highlight_dark ) -{ - mShadowDark = shadow_dark; - mShadowLight = shadow_light; - mHighlightLight = highlight_light; - mHighlightDark = highlight_dark; -} - -void LLViewBorder::setTexture( const LLUUID &image_id ) -{ - mTexture = LLUI::getUIImageByID(image_id); -} - - -void LLViewBorder::draw() -{ - if( STYLE_LINE == mStyle ) - { - if( 0 == mBorderWidth ) - { - // no visible border - } - else - if( 1 == mBorderWidth ) - { - drawOnePixelLines(); - } - else - if( 2 == mBorderWidth ) - { - drawTwoPixelLines(); - } - else - { - llassert( false ); // not implemented - } - } - - LLView::draw(); -} - -void LLViewBorder::drawOnePixelLines() -{ - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLColor4 top_color = mHighlightLight.get(); - LLColor4 bottom_color = mHighlightLight.get(); - switch( mBevel ) - { - case BEVEL_OUT: - top_color = mHighlightLight.get(); - bottom_color = mShadowDark.get(); - break; - case BEVEL_IN: - top_color = mShadowDark.get(); - bottom_color = mHighlightLight.get(); - break; - case BEVEL_NONE: - // use defaults - break; - default: - llassert(0); - } - - if( mHasKeyboardFocus ) - { - top_color = gFocusMgr.getFocusColor(); - bottom_color = top_color; - - LLUI::setLineWidth(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt())); - } - - S32 left = 0; - S32 top = getRect().getHeight(); - S32 right = getRect().getWidth(); - S32 bottom = 0; - - gGL.color4fv( top_color.mV ); - gl_line_2d(left, bottom, left, top); - gl_line_2d(left, top, right, top); - - gGL.color4fv( bottom_color.mV ); - gl_line_2d(right, top, right, bottom); - gl_line_2d(left, bottom, right, bottom); - - LLUI::setLineWidth(1.f); -} - -void LLViewBorder::drawTwoPixelLines() -{ - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLColor4 focus_color = gFocusMgr.getFocusColor(); - - LLColor4 top_in_color; - LLColor4 top_out_color; - LLColor4 bottom_in_color; - LLColor4 bottom_out_color; - - switch( mBevel ) - { - case BEVEL_OUT: - top_in_color = mHighlightLight.get(); - top_out_color = mHighlightDark.get(); - bottom_in_color = mShadowLight.get(); - bottom_out_color = mShadowDark.get(); - break; - case BEVEL_IN: - top_in_color = mShadowDark.get(); - top_out_color = mShadowLight.get(); - bottom_in_color = mHighlightDark.get(); - bottom_out_color = mHighlightLight.get(); - break; - case BEVEL_BRIGHT: - top_in_color = mHighlightLight.get(); - top_out_color = mHighlightLight.get(); - bottom_in_color = mHighlightLight.get(); - bottom_out_color = mHighlightLight.get(); - break; - case BEVEL_NONE: - top_in_color = mShadowDark.get(); - top_out_color = mShadowDark.get(); - bottom_in_color = mShadowDark.get(); - bottom_out_color = mShadowDark.get(); - // use defaults - break; - default: - llassert(0); - } - - if( mHasKeyboardFocus ) - { - top_out_color = focus_color; - bottom_out_color = focus_color; - } - - S32 left = 0; - S32 top = getRect().getHeight(); - S32 right = getRect().getWidth(); - S32 bottom = 0; - - // draw borders - gGL.color3fv( top_out_color.mV ); - gl_line_2d(left, bottom, left, top-1); - gl_line_2d(left, top-1, right, top-1); - - gGL.color3fv( top_in_color.mV ); - gl_line_2d(left+1, bottom+1, left+1, top-2); - gl_line_2d(left+1, top-2, right-1, top-2); - - gGL.color3fv( bottom_out_color.mV ); - gl_line_2d(right-1, top-1, right-1, bottom); - gl_line_2d(left, bottom, right, bottom); - - gGL.color3fv( bottom_in_color.mV ); - gl_line_2d(right-2, top-2, right-2, bottom+1); - gl_line_2d(left+1, bottom+1, right-1, bottom+1); -} - -bool LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style) -{ - if (node->hasAttribute("bevel_style")) - { - std::string bevel_string; - node->getAttributeString("bevel_style", bevel_string); - LLStringUtil::toLower(bevel_string); - - if (bevel_string == "none") - { - bevel_style = LLViewBorder::BEVEL_NONE; - } - else if (bevel_string == "in") - { - bevel_style = LLViewBorder::BEVEL_IN; - } - else if (bevel_string == "out") - { - bevel_style = LLViewBorder::BEVEL_OUT; - } - else if (bevel_string == "bright") - { - bevel_style = LLViewBorder::BEVEL_BRIGHT; - } - return true; - } - return false; -} - +/** + * @file llviewborder.cpp + * + * $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 "linden_common.h" +#include "llviewborder.h" +#include "llrender.h" +#include "llfocusmgr.h" +#include "lluictrlfactory.h" +#include "lluiimage.h" + +static LLDefaultChildRegistry::Register r("view_border"); + +void LLViewBorder::BevelValues::declareValues() +{ + declare("in", LLViewBorder::BEVEL_IN); + declare("out", LLViewBorder::BEVEL_OUT); + declare("bright", LLViewBorder::BEVEL_BRIGHT); + declare("none", LLViewBorder::BEVEL_NONE); +} + +void LLViewBorder::StyleValues::declareValues() +{ + declare("line", LLViewBorder::STYLE_LINE); + declare("texture", LLViewBorder::STYLE_TEXTURE); +} + +LLViewBorder::Params::Params() +: bevel_style("bevel_style", BEVEL_OUT), + render_style("border_style", STYLE_LINE), + border_thickness("border_thickness"), + highlight_light_color("highlight_light_color"), + highlight_dark_color("highlight_dark_color"), + shadow_light_color("shadow_light_color"), + shadow_dark_color("shadow_dark_color") +{ + addSynonym(border_thickness, "thickness"); + addSynonym(render_style, "style"); +} + + +LLViewBorder::LLViewBorder(const LLViewBorder::Params& p) +: LLView(p), + mTexture( NULL ), + mHasKeyboardFocus( false ), + mBorderWidth(p.border_thickness), + mHighlightLight(p.highlight_light_color()), + mHighlightDark(p.highlight_dark_color()), + mShadowLight(p.shadow_light_color()), + mShadowDark(p.shadow_dark_color()), + mBevel(p.bevel_style), + mStyle(p.render_style) +{} + +void LLViewBorder::setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light ) +{ + mShadowDark = shadow_dark; + mHighlightLight = highlight_light; +} + +void LLViewBorder::setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark, + const LLColor4& highlight_light, const LLColor4& highlight_dark ) +{ + mShadowDark = shadow_dark; + mShadowLight = shadow_light; + mHighlightLight = highlight_light; + mHighlightDark = highlight_dark; +} + +void LLViewBorder::setTexture( const LLUUID &image_id ) +{ + mTexture = LLUI::getUIImageByID(image_id); +} + + +void LLViewBorder::draw() +{ + if( STYLE_LINE == mStyle ) + { + if( 0 == mBorderWidth ) + { + // no visible border + } + else + if( 1 == mBorderWidth ) + { + drawOnePixelLines(); + } + else + if( 2 == mBorderWidth ) + { + drawTwoPixelLines(); + } + else + { + llassert( false ); // not implemented + } + } + + LLView::draw(); +} + +void LLViewBorder::drawOnePixelLines() +{ + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLColor4 top_color = mHighlightLight.get(); + LLColor4 bottom_color = mHighlightLight.get(); + switch( mBevel ) + { + case BEVEL_OUT: + top_color = mHighlightLight.get(); + bottom_color = mShadowDark.get(); + break; + case BEVEL_IN: + top_color = mShadowDark.get(); + bottom_color = mHighlightLight.get(); + break; + case BEVEL_NONE: + // use defaults + break; + default: + llassert(0); + } + + if( mHasKeyboardFocus ) + { + top_color = gFocusMgr.getFocusColor(); + bottom_color = top_color; + + LLUI::setLineWidth(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt())); + } + + S32 left = 0; + S32 top = getRect().getHeight(); + S32 right = getRect().getWidth(); + S32 bottom = 0; + + gGL.color4fv( top_color.mV ); + gl_line_2d(left, bottom, left, top); + gl_line_2d(left, top, right, top); + + gGL.color4fv( bottom_color.mV ); + gl_line_2d(right, top, right, bottom); + gl_line_2d(left, bottom, right, bottom); + + LLUI::setLineWidth(1.f); +} + +void LLViewBorder::drawTwoPixelLines() +{ + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLColor4 focus_color = gFocusMgr.getFocusColor(); + + LLColor4 top_in_color; + LLColor4 top_out_color; + LLColor4 bottom_in_color; + LLColor4 bottom_out_color; + + switch( mBevel ) + { + case BEVEL_OUT: + top_in_color = mHighlightLight.get(); + top_out_color = mHighlightDark.get(); + bottom_in_color = mShadowLight.get(); + bottom_out_color = mShadowDark.get(); + break; + case BEVEL_IN: + top_in_color = mShadowDark.get(); + top_out_color = mShadowLight.get(); + bottom_in_color = mHighlightDark.get(); + bottom_out_color = mHighlightLight.get(); + break; + case BEVEL_BRIGHT: + top_in_color = mHighlightLight.get(); + top_out_color = mHighlightLight.get(); + bottom_in_color = mHighlightLight.get(); + bottom_out_color = mHighlightLight.get(); + break; + case BEVEL_NONE: + top_in_color = mShadowDark.get(); + top_out_color = mShadowDark.get(); + bottom_in_color = mShadowDark.get(); + bottom_out_color = mShadowDark.get(); + // use defaults + break; + default: + llassert(0); + } + + if( mHasKeyboardFocus ) + { + top_out_color = focus_color; + bottom_out_color = focus_color; + } + + S32 left = 0; + S32 top = getRect().getHeight(); + S32 right = getRect().getWidth(); + S32 bottom = 0; + + // draw borders + gGL.color3fv( top_out_color.mV ); + gl_line_2d(left, bottom, left, top-1); + gl_line_2d(left, top-1, right, top-1); + + gGL.color3fv( top_in_color.mV ); + gl_line_2d(left+1, bottom+1, left+1, top-2); + gl_line_2d(left+1, top-2, right-1, top-2); + + gGL.color3fv( bottom_out_color.mV ); + gl_line_2d(right-1, top-1, right-1, bottom); + gl_line_2d(left, bottom, right, bottom); + + gGL.color3fv( bottom_in_color.mV ); + gl_line_2d(right-2, top-2, right-2, bottom+1); + gl_line_2d(left+1, bottom+1, right-1, bottom+1); +} + +bool LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style) +{ + if (node->hasAttribute("bevel_style")) + { + std::string bevel_string; + node->getAttributeString("bevel_style", bevel_string); + LLStringUtil::toLower(bevel_string); + + if (bevel_string == "none") + { + bevel_style = LLViewBorder::BEVEL_NONE; + } + else if (bevel_string == "in") + { + bevel_style = LLViewBorder::BEVEL_IN; + } + else if (bevel_string == "out") + { + bevel_style = LLViewBorder::BEVEL_OUT; + } + else if (bevel_string == "bright") + { + bevel_style = LLViewBorder::BEVEL_BRIGHT; + } + return true; + } + return false; +} + diff --git a/indra/llui/llviewborder.h b/indra/llui/llviewborder.h index 6a2bcb4b20..be499bb534 100644 --- a/indra/llui/llviewborder.h +++ b/indra/llui/llviewborder.h @@ -1,110 +1,110 @@ -/** - * @file llviewborder.h - * @brief A customizable decorative border. Does not interact with mouse events. - * - * $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$ - */ - -#ifndef LL_LLVIEWBORDER_H -#define LL_LLVIEWBORDER_H - -#include "llview.h" - -class LLViewBorder : public LLView -{ -public: - typedef enum e_bevel { BEVEL_IN, BEVEL_OUT, BEVEL_BRIGHT, BEVEL_NONE } EBevel ; - typedef enum e_style { STYLE_LINE, STYLE_TEXTURE } EStyle; - - struct BevelValues - : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - struct StyleValues - : public LLInitParam::TypeValuesHelper - { - static void declareValues(); - }; - - struct Params : public LLInitParam::Block - { - Optional bevel_style; - Optional render_style; - Optional border_thickness; - - Optional highlight_light_color, - highlight_dark_color, - shadow_light_color, - shadow_dark_color; - - Params(); - }; -protected: - LLViewBorder(const Params&); - friend class LLUICtrlFactory; -public: - virtual void setValue(const LLSD& val) { setRect(LLRect(val)); } - - virtual bool isCtrl() const { return false; } - - // llview functionality - virtual void draw(); - - static bool getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style); - - void setBorderWidth(S32 width) { mBorderWidth = width; } - S32 getBorderWidth() const { return mBorderWidth; } - void setBevel(EBevel bevel) { mBevel = bevel; } - EBevel getBevel() const { return mBevel; } - void setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light ); - void setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark, - const LLColor4& highlight_light, const LLColor4& highlight_dark ); - void setTexture( const class LLUUID &image_id ); - - LLColor4 getHighlightLight() {return mHighlightLight.get();} - LLColor4 getShadowDark() {return mHighlightDark.get();} - - EStyle getStyle() const { return mStyle; } - - void setKeyboardFocusHighlight( bool b ) { mHasKeyboardFocus = b; } - -private: - void drawOnePixelLines(); - void drawTwoPixelLines(); - void drawTextures(); - - EBevel mBevel; - EStyle mStyle; - LLUIColor mHighlightLight; - LLUIColor mHighlightDark; - LLUIColor mShadowLight; - LLUIColor mShadowDark; - LLUIColor mBackgroundColor; - S32 mBorderWidth; - LLPointer mTexture; - bool mHasKeyboardFocus; -}; - -#endif // LL_LLVIEWBORDER_H - +/** + * @file llviewborder.h + * @brief A customizable decorative border. Does not interact with mouse events. + * + * $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$ + */ + +#ifndef LL_LLVIEWBORDER_H +#define LL_LLVIEWBORDER_H + +#include "llview.h" + +class LLViewBorder : public LLView +{ +public: + typedef enum e_bevel { BEVEL_IN, BEVEL_OUT, BEVEL_BRIGHT, BEVEL_NONE } EBevel ; + typedef enum e_style { STYLE_LINE, STYLE_TEXTURE } EStyle; + + struct BevelValues + : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + struct StyleValues + : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + + struct Params : public LLInitParam::Block + { + Optional bevel_style; + Optional render_style; + Optional border_thickness; + + Optional highlight_light_color, + highlight_dark_color, + shadow_light_color, + shadow_dark_color; + + Params(); + }; +protected: + LLViewBorder(const Params&); + friend class LLUICtrlFactory; +public: + virtual void setValue(const LLSD& val) { setRect(LLRect(val)); } + + virtual bool isCtrl() const { return false; } + + // llview functionality + virtual void draw(); + + static bool getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style); + + void setBorderWidth(S32 width) { mBorderWidth = width; } + S32 getBorderWidth() const { return mBorderWidth; } + void setBevel(EBevel bevel) { mBevel = bevel; } + EBevel getBevel() const { return mBevel; } + void setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light ); + void setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark, + const LLColor4& highlight_light, const LLColor4& highlight_dark ); + void setTexture( const class LLUUID &image_id ); + + LLColor4 getHighlightLight() {return mHighlightLight.get();} + LLColor4 getShadowDark() {return mHighlightDark.get();} + + EStyle getStyle() const { return mStyle; } + + void setKeyboardFocusHighlight( bool b ) { mHasKeyboardFocus = b; } + +private: + void drawOnePixelLines(); + void drawTwoPixelLines(); + void drawTextures(); + + EBevel mBevel; + EStyle mStyle; + LLUIColor mHighlightLight; + LLUIColor mHighlightDark; + LLUIColor mShadowLight; + LLUIColor mShadowDark; + LLUIColor mBackgroundColor; + S32 mBorderWidth; + LLPointer mTexture; + bool mHasKeyboardFocus; +}; + +#endif // LL_LLVIEWBORDER_H + diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp index 177cfb98ce..1bf3e3c43b 100644 --- a/indra/llui/llviewereventrecorder.cpp +++ b/indra/llui/llviewereventrecorder.cpp @@ -1,296 +1,296 @@ -/** - * @file llviewereventrecorder.cpp - * @brief Viewer event recording and playback support for mouse and keyboard events - * - * $LicenseInfo:firstyear=2013&license=viewerlgpl$ - * - * Copyright (c) 2013, 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 "llviewereventrecorder.h" -#include "llui.h" -#include "llleap.h" - -LLViewerEventRecorder::LLViewerEventRecorder() { - - clear(UNDEFINED); - logEvents = false; - // Remove any previous event log file - std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old"); - LLFile::remove(old_log_ui_events_to_llsd_file, ENOENT); - - - mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd"); - LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT); - -} - - -bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() { - return LLUI::getInstance()->mSettingGroups["config"]->getBOOL("ShowEventRecorderMenuItems"); -} - - -void LLViewerEventRecorder::setEventLoggingOn() { - if (! mLog.is_open()) { - mLog.open(mLogFilename.c_str(), std::ios_base::out); - } - logEvents=true; - LL_DEBUGS() << "LLViewerEventRecorder::setEventLoggingOn event logging turned on" << LL_ENDL; -} - -void LLViewerEventRecorder::setEventLoggingOff() { - logEvents=false; - mLog.flush(); - mLog.close(); - LL_DEBUGS() << "LLViewerEventRecorder::setEventLoggingOff event logging turned off" << LL_ENDL; -} - - - LLViewerEventRecorder::~LLViewerEventRecorder() { - if (mLog.is_open()) { - mLog.close(); - } -} - -void LLViewerEventRecorder::clear_xui() { - xui.clear(); -} - -void LLViewerEventRecorder::clear(S32 r) { - - xui.clear(); - - local_x=r; - local_y=r; - - global_x=r; - global_y=r; - - -} - -void LLViewerEventRecorder::setMouseLocalCoords(S32 x, S32 y) { - local_x=x; - local_y=y; -} - -void LLViewerEventRecorder::setMouseGlobalCoords(S32 x, S32 y) { - global_x=x; - global_y=y; -} - -void LLViewerEventRecorder::updateMouseEventInfo(S32 local_x, S32 local_y, S32 global_x, S32 global_y, std::string mName) { - - LLView * target_view = LLUI::getInstance()->resolvePath(LLUI::getInstance()->getRootView(), xui); - if (! target_view) { - LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo - xui path on file at moment is NOT valid - so DO NOT record these local coords" << LL_ENDL; - return; - } - LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo b4 updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << LL_ENDL; - - - if (this->local_x < 1 && this->local_y<1 && local_x && local_y) { - this->local_x=local_x; - this->local_y=local_y; - } - this->global_x=global_x; - this->global_y=global_y; - - // ONLY record deepest xui path for hierarchy searches - or first/only xui for floaters/panels reached via mouse captor - and llmousehandler - if (mName!="" && mName!="/" && xui=="") { - // xui=std::string("/")+mName+xui; - //xui=mName+xui; - xui = mName; // TODO review confirm we never call with partial path - also cAN REMOVE CHECK FOR "" - ON OTHER HAND IT'S PRETTY HARMLESS - } - - LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo after updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << LL_ENDL; -} - -void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, bool visibility, std::string event_subtype) { - - LLSD event=LLSD::emptyMap(); - - event.insert("event",LLSD(std::string("visibility"))); - - if (visibility) { - event.insert("visibility",LLSD(true)); - } else { - event.insert("visibility",LLSD(false)); - } - - if (event_subtype!="") { - event.insert("event_subtype", LLSD(event_subtype)); - } - - if(name!="") { - event.insert("name",LLSD(name)); - } - - if (xui!="") { - event.insert("path",LLSD(xui)); - } - - event.insert("timestamp",LLSD(LLDate::now().asString())); - recordEvent(event); -} - - -std::string LLViewerEventRecorder::get_xui() { - return xui; -} -void LLViewerEventRecorder::update_xui(std::string xui) { - if (xui!="" && this->xui=="" ) { - LL_DEBUGS() << "LLViewerEventRecorder::update_xui to " << xui << LL_ENDL; - this->xui=xui; - } else { - LL_DEBUGS() << "LLViewerEventRecorder::update_xui called with empty string" << LL_ENDL; - } -} - -void LLViewerEventRecorder::logKeyEvent(KEY key, MASK mask) { - - // NOTE: Event recording only logs keydown events - the viewer itself hides keyup events at a fairly low level in the code and does not appear to care about them anywhere - - LLSD event = LLSD::emptyMap(); - - event.insert("event",LLSD("type")); - - // keysym ...or - // keycode...or - // char - event.insert("keysym",LLSD(LLKeyboard::stringFromKey(key))); - - // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps - // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might - // break the test script and it would be useful to have more context to make these sorts of edits safer - - // TODO replace this with a call which extracts to an array of names of masks (just like vita expects during playback) - // This is looking more and more like an object is a good idea, for this part a handy method call to setMask(mask) would be nice :-) - // call the func - llkeyboard::llsdStringarrayFromMask - - LLSD key_mask=LLSD::emptyArray(); - - if (mask & MASK_CONTROL) { key_mask.append(LLSD("CTL")); } // Mac command key - has code of 0x1 in llcommon/indra_contstants - if (mask & MASK_ALT) { key_mask.append(LLSD("ALT")); } - if (mask & MASK_SHIFT) { key_mask.append(LLSD("SHIFT")); } - if (mask & MASK_MAC_CONTROL) { key_mask.append(LLSD("MAC_CONTROL")); } - - event.insert("mask",key_mask); - event.insert("timestamp",LLSD(LLDate::now().asString())); - - // Although vita has keyDown and keyUp requests it does not have type as a high-level concept - // (maybe it should) - instead it has a convenience method that generates the keydown and keyup events - // Here we will use "type" as our event type - - LL_DEBUGS() << "LLVIewerEventRecorder::logKeyEvent Serialized LLSD for event " << event.asString() << "\n" << LL_ENDL; - - - //LL_DEBUGS() << "[VITA] key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << "handled by " << getName() << LL_ENDL; - LL_DEBUGS() << "LLVIewerEventRecorder::logKeyEvent key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << LL_ENDL; - - - recordEvent(event); - -} - -void LLViewerEventRecorder::playbackRecording() { - - LLSD LeapCommand; - - // ivita sets this on startup, it also sends commands to the viewer to make start, stop, and playback menu items visible in viewer - LeapCommand =LLUI::getInstance()->mSettingGroups["config"]->getLLSD("LeapPlaybackEventsCommand"); - - LL_DEBUGS() << "[VITA] launching playback - leap command is: " << LLSDXMLStreamer(LeapCommand) << LL_ENDL; - LLLeap::create("", LeapCommand, false); // exception=false - -} - - -void LLViewerEventRecorder::recordEvent(LLSD event) { - LL_DEBUGS() << "LLViewerEventRecorder::recordEvent event written to log: " << LLSDXMLStreamer(event) << LL_ENDL; - mLog << event << std::endl; - -} -void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) { - if (! logEvents) return; - - // Note: keyUp is not captured since the viewer seems to not care about keyUp events - - LLSD event=LLSD::emptyMap(); - - event.insert("timestamp",LLSD(LLDate::now().asString())); - - - // keysym ...or - // keycode...or - // char - - LL_DEBUGS() << "Wrapped in conversion to wstring " << wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << LL_ENDL; - - event.insert("char", - LLSD( wstring_to_utf8str(LLWString( 1,uni_char)) ) - ); - - // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps - // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might - // break the test script and it would be useful to have more context to make these sorts of edits safer - - // TODO need to consider mask keys too? Doesn't seem possible - at least not easily at this point - - event.insert("event",LLSD("keyDown")); - - LL_DEBUGS() << "[VITA] unicode key: " << uni_char << LL_ENDL; - LL_DEBUGS() << "[VITA] dumpxml " << LLSDXMLStreamer(event) << "\n" << LL_ENDL; - - - recordEvent(event); - -} - -void LLViewerEventRecorder::logMouseEvent(std::string button_state,std::string button_name) -{ - if (! logEvents) return; - - LLSD event=LLSD::emptyMap(); - - event.insert("event",LLSD(std::string("mouse"+ button_state))); - event.insert("button",LLSD(button_name)); - if (xui!="") { - event.insert("path",LLSD(xui)); - } - - if (local_x>0 && local_y>0) { - event.insert("local_x",LLSD(local_x)); - event.insert("local_y",LLSD(local_y)); - } - - if (global_x>0 && global_y>0) { - event.insert("global_x",LLSD(global_x)); - event.insert("global_y",LLSD(global_y)); - } - event.insert("timestamp",LLSD(LLDate::now().asString())); - recordEvent(event); - - - clear(UNDEFINED); - - -} +/** + * @file llviewereventrecorder.cpp + * @brief Viewer event recording and playback support for mouse and keyboard events + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * + * Copyright (c) 2013, 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 "llviewereventrecorder.h" +#include "llui.h" +#include "llleap.h" + +LLViewerEventRecorder::LLViewerEventRecorder() { + + clear(UNDEFINED); + logEvents = false; + // Remove any previous event log file + std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old"); + LLFile::remove(old_log_ui_events_to_llsd_file, ENOENT); + + + mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd"); + LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT); + +} + + +bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() { + return LLUI::getInstance()->mSettingGroups["config"]->getBOOL("ShowEventRecorderMenuItems"); +} + + +void LLViewerEventRecorder::setEventLoggingOn() { + if (! mLog.is_open()) { + mLog.open(mLogFilename.c_str(), std::ios_base::out); + } + logEvents=true; + LL_DEBUGS() << "LLViewerEventRecorder::setEventLoggingOn event logging turned on" << LL_ENDL; +} + +void LLViewerEventRecorder::setEventLoggingOff() { + logEvents=false; + mLog.flush(); + mLog.close(); + LL_DEBUGS() << "LLViewerEventRecorder::setEventLoggingOff event logging turned off" << LL_ENDL; +} + + + LLViewerEventRecorder::~LLViewerEventRecorder() { + if (mLog.is_open()) { + mLog.close(); + } +} + +void LLViewerEventRecorder::clear_xui() { + xui.clear(); +} + +void LLViewerEventRecorder::clear(S32 r) { + + xui.clear(); + + local_x=r; + local_y=r; + + global_x=r; + global_y=r; + + +} + +void LLViewerEventRecorder::setMouseLocalCoords(S32 x, S32 y) { + local_x=x; + local_y=y; +} + +void LLViewerEventRecorder::setMouseGlobalCoords(S32 x, S32 y) { + global_x=x; + global_y=y; +} + +void LLViewerEventRecorder::updateMouseEventInfo(S32 local_x, S32 local_y, S32 global_x, S32 global_y, std::string mName) { + + LLView * target_view = LLUI::getInstance()->resolvePath(LLUI::getInstance()->getRootView(), xui); + if (! target_view) { + LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo - xui path on file at moment is NOT valid - so DO NOT record these local coords" << LL_ENDL; + return; + } + LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo b4 updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << LL_ENDL; + + + if (this->local_x < 1 && this->local_y<1 && local_x && local_y) { + this->local_x=local_x; + this->local_y=local_y; + } + this->global_x=global_x; + this->global_y=global_y; + + // ONLY record deepest xui path for hierarchy searches - or first/only xui for floaters/panels reached via mouse captor - and llmousehandler + if (mName!="" && mName!="/" && xui=="") { + // xui=std::string("/")+mName+xui; + //xui=mName+xui; + xui = mName; // TODO review confirm we never call with partial path - also cAN REMOVE CHECK FOR "" - ON OTHER HAND IT'S PRETTY HARMLESS + } + + LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo after updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << LL_ENDL; +} + +void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, bool visibility, std::string event_subtype) { + + LLSD event=LLSD::emptyMap(); + + event.insert("event",LLSD(std::string("visibility"))); + + if (visibility) { + event.insert("visibility",LLSD(true)); + } else { + event.insert("visibility",LLSD(false)); + } + + if (event_subtype!="") { + event.insert("event_subtype", LLSD(event_subtype)); + } + + if(name!="") { + event.insert("name",LLSD(name)); + } + + if (xui!="") { + event.insert("path",LLSD(xui)); + } + + event.insert("timestamp",LLSD(LLDate::now().asString())); + recordEvent(event); +} + + +std::string LLViewerEventRecorder::get_xui() { + return xui; +} +void LLViewerEventRecorder::update_xui(std::string xui) { + if (xui!="" && this->xui=="" ) { + LL_DEBUGS() << "LLViewerEventRecorder::update_xui to " << xui << LL_ENDL; + this->xui=xui; + } else { + LL_DEBUGS() << "LLViewerEventRecorder::update_xui called with empty string" << LL_ENDL; + } +} + +void LLViewerEventRecorder::logKeyEvent(KEY key, MASK mask) { + + // NOTE: Event recording only logs keydown events - the viewer itself hides keyup events at a fairly low level in the code and does not appear to care about them anywhere + + LLSD event = LLSD::emptyMap(); + + event.insert("event",LLSD("type")); + + // keysym ...or + // keycode...or + // char + event.insert("keysym",LLSD(LLKeyboard::stringFromKey(key))); + + // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps + // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might + // break the test script and it would be useful to have more context to make these sorts of edits safer + + // TODO replace this with a call which extracts to an array of names of masks (just like vita expects during playback) + // This is looking more and more like an object is a good idea, for this part a handy method call to setMask(mask) would be nice :-) + // call the func - llkeyboard::llsdStringarrayFromMask + + LLSD key_mask=LLSD::emptyArray(); + + if (mask & MASK_CONTROL) { key_mask.append(LLSD("CTL")); } // Mac command key - has code of 0x1 in llcommon/indra_contstants + if (mask & MASK_ALT) { key_mask.append(LLSD("ALT")); } + if (mask & MASK_SHIFT) { key_mask.append(LLSD("SHIFT")); } + if (mask & MASK_MAC_CONTROL) { key_mask.append(LLSD("MAC_CONTROL")); } + + event.insert("mask",key_mask); + event.insert("timestamp",LLSD(LLDate::now().asString())); + + // Although vita has keyDown and keyUp requests it does not have type as a high-level concept + // (maybe it should) - instead it has a convenience method that generates the keydown and keyup events + // Here we will use "type" as our event type + + LL_DEBUGS() << "LLVIewerEventRecorder::logKeyEvent Serialized LLSD for event " << event.asString() << "\n" << LL_ENDL; + + + //LL_DEBUGS() << "[VITA] key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << "handled by " << getName() << LL_ENDL; + LL_DEBUGS() << "LLVIewerEventRecorder::logKeyEvent key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << LL_ENDL; + + + recordEvent(event); + +} + +void LLViewerEventRecorder::playbackRecording() { + + LLSD LeapCommand; + + // ivita sets this on startup, it also sends commands to the viewer to make start, stop, and playback menu items visible in viewer + LeapCommand =LLUI::getInstance()->mSettingGroups["config"]->getLLSD("LeapPlaybackEventsCommand"); + + LL_DEBUGS() << "[VITA] launching playback - leap command is: " << LLSDXMLStreamer(LeapCommand) << LL_ENDL; + LLLeap::create("", LeapCommand, false); // exception=false + +} + + +void LLViewerEventRecorder::recordEvent(LLSD event) { + LL_DEBUGS() << "LLViewerEventRecorder::recordEvent event written to log: " << LLSDXMLStreamer(event) << LL_ENDL; + mLog << event << std::endl; + +} +void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) { + if (! logEvents) return; + + // Note: keyUp is not captured since the viewer seems to not care about keyUp events + + LLSD event=LLSD::emptyMap(); + + event.insert("timestamp",LLSD(LLDate::now().asString())); + + + // keysym ...or + // keycode...or + // char + + LL_DEBUGS() << "Wrapped in conversion to wstring " << wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << LL_ENDL; + + event.insert("char", + LLSD( wstring_to_utf8str(LLWString( 1,uni_char)) ) + ); + + // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps + // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might + // break the test script and it would be useful to have more context to make these sorts of edits safer + + // TODO need to consider mask keys too? Doesn't seem possible - at least not easily at this point + + event.insert("event",LLSD("keyDown")); + + LL_DEBUGS() << "[VITA] unicode key: " << uni_char << LL_ENDL; + LL_DEBUGS() << "[VITA] dumpxml " << LLSDXMLStreamer(event) << "\n" << LL_ENDL; + + + recordEvent(event); + +} + +void LLViewerEventRecorder::logMouseEvent(std::string button_state,std::string button_name) +{ + if (! logEvents) return; + + LLSD event=LLSD::emptyMap(); + + event.insert("event",LLSD(std::string("mouse"+ button_state))); + event.insert("button",LLSD(button_name)); + if (xui!="") { + event.insert("path",LLSD(xui)); + } + + if (local_x>0 && local_y>0) { + event.insert("local_x",LLSD(local_x)); + event.insert("local_y",LLSD(local_y)); + } + + if (global_x>0 && global_y>0) { + event.insert("global_x",LLSD(global_x)); + event.insert("global_y",LLSD(global_y)); + } + event.insert("timestamp",LLSD(LLDate::now().asString())); + recordEvent(event); + + + clear(UNDEFINED); + + +} diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index 336d884553..4835a60e77 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -1,136 +1,136 @@ -/** - * @file llviewquery.cpp - * @brief Implementation of view query class. - * - * $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 "linden_common.h" - -#include "llview.h" -#include "lluictrl.h" -#include "llviewquery.h" - -void LLQuerySorter::sort(LLView * parent, viewList_t &children) const {} - -filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const -{ - return filterResult_t(children.empty(), true); -} - -filterResult_t LLRootsFilter::operator() (const LLView* const view, const viewList_t & children) const -{ - return filterResult_t(true, false); -} - -filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const -{ - return filterResult_t(view->getVisible(), view->getVisible()); -} -filterResult_t LLEnabledFilter::operator() (const LLView* const view, const viewList_t & children) const -{ - return filterResult_t(view->getEnabled(), view->getEnabled()); -} -filterResult_t LLTabStopFilter::operator() (const LLView* const view, const viewList_t & children) const -{ - return filterResult_t(view->isCtrl() && static_cast(view)->hasTabStop(), - view->canFocusChildren()); -} - -filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewList_t & children) const -{ - return filterResult_t(view->isCtrl(),true); -} - -// -// LLViewQuery -// - -viewList_t LLViewQuery::run(LLView* view) const -{ - viewList_t result; - - // prefilter gets immediate children of view - filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters); - if(!pre.first && !pre.second) - { - // not including ourselves or the children - // nothing more to do - return result; - } - - viewList_t filtered_children; - filterResult_t post(true, true); - if(pre.second) - { - // run filters on children - filterChildren(view, filtered_children); - // only run post filters if this element passed pre filters - // so if you failed to pass the pre filter, you can't filter out children in post - if (pre.first) - { - post = runFilters(view, filtered_children, mPostFilters); - } - } - - if(pre.first && post.first) - { - result.push_back(view); - } - - if(pre.second && post.second) - { - result.insert(result.end(), filtered_children.begin(), filtered_children.end()); - } - - return result; -} - -void LLViewQuery::filterChildren(LLView* parent_view, viewList_t & filtered_children) const -{ - LLView::child_list_t views(*(parent_view->getChildList())); - if (mSorterp) - { - mSorterp->sort(parent_view, views); // sort the children per the sorter - } - for(LLView::child_list_iter_t iter = views.begin(); - iter != views.end(); - iter++) - { - viewList_t indiv_children = this->run(*iter); - filtered_children.splice(filtered_children.end(), indiv_children); - } -} - -filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const -{ - filterResult_t result = filterResult_t(true, true); - for(filterList_const_iter_t iter = filters.begin(); - iter != filters.end(); - iter++) - { - filterResult_t filtered = (**iter)(view, children); - result.first = result.first && filtered.first; - result.second = result.second && filtered.second; - } - return result; -} +/** + * @file llviewquery.cpp + * @brief Implementation of view query class. + * + * $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 "linden_common.h" + +#include "llview.h" +#include "lluictrl.h" +#include "llviewquery.h" + +void LLQuerySorter::sort(LLView * parent, viewList_t &children) const {} + +filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(children.empty(), true); +} + +filterResult_t LLRootsFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(true, false); +} + +filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->getVisible(), view->getVisible()); +} +filterResult_t LLEnabledFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->getEnabled(), view->getEnabled()); +} +filterResult_t LLTabStopFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->isCtrl() && static_cast(view)->hasTabStop(), + view->canFocusChildren()); +} + +filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->isCtrl(),true); +} + +// +// LLViewQuery +// + +viewList_t LLViewQuery::run(LLView* view) const +{ + viewList_t result; + + // prefilter gets immediate children of view + filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters); + if(!pre.first && !pre.second) + { + // not including ourselves or the children + // nothing more to do + return result; + } + + viewList_t filtered_children; + filterResult_t post(true, true); + if(pre.second) + { + // run filters on children + filterChildren(view, filtered_children); + // only run post filters if this element passed pre filters + // so if you failed to pass the pre filter, you can't filter out children in post + if (pre.first) + { + post = runFilters(view, filtered_children, mPostFilters); + } + } + + if(pre.first && post.first) + { + result.push_back(view); + } + + if(pre.second && post.second) + { + result.insert(result.end(), filtered_children.begin(), filtered_children.end()); + } + + return result; +} + +void LLViewQuery::filterChildren(LLView* parent_view, viewList_t & filtered_children) const +{ + LLView::child_list_t views(*(parent_view->getChildList())); + if (mSorterp) + { + mSorterp->sort(parent_view, views); // sort the children per the sorter + } + for(LLView::child_list_iter_t iter = views.begin(); + iter != views.end(); + iter++) + { + viewList_t indiv_children = this->run(*iter); + filtered_children.splice(filtered_children.end(), indiv_children); + } +} + +filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const +{ + filterResult_t result = filterResult_t(true, true); + for(filterList_const_iter_t iter = filters.begin(); + iter != filters.end(); + iter++) + { + filterResult_t filtered = (**iter)(view, children); + result.first = result.first && filtered.first; + result.second = result.second && filtered.second; + } + return result; +} diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h index 59c580c08d..c03a15e8f9 100644 --- a/indra/llui/llviewquery.h +++ b/indra/llui/llviewquery.h @@ -1,137 +1,137 @@ -/** - * @file llviewquery.h - * @brief Query algorithm for flattening and filtering the view hierarchy. - * - * $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$ - */ - -#ifndef LL_LLVIEWQUERY_H -#define LL_LLVIEWQUERY_H - -#include - -#include "llsingleton.h" -#include "llui.h" - -class LLView; - -typedef std::list viewList_t; -typedef std::pair filterResult_t; - -// Abstract base class for all query filters. -class LLQueryFilter -{ -public: - virtual ~LLQueryFilter() {}; - virtual filterResult_t operator() (const LLView* const view, const viewList_t & children) const = 0; -}; - -class LLQuerySorter -{ -public: - virtual ~LLQuerySorter() {}; - virtual void sort(LLView * parent, viewList_t &children) const; -}; - -class LLLeavesFilter : public LLQueryFilter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(LLLeavesFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; -}; - -class LLRootsFilter : public LLQueryFilter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(LLRootsFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; -}; - -class LLVisibleFilter : public LLQueryFilter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(LLVisibleFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; -}; - -class LLEnabledFilter : public LLQueryFilter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(LLEnabledFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; -}; - -class LLTabStopFilter : public LLQueryFilter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(LLTabStopFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; -}; - -class LLCtrlFilter : public LLQueryFilter, public LLSingleton -{ - LLSINGLETON_EMPTY_CTOR(LLCtrlFilter); - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; -}; - -template -class LLWidgetTypeFilter : public LLQueryFilter -{ - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const - { - return filterResult_t(dynamic_cast(view) != NULL, true); - } - -}; - -// Algorithm for flattening -class LLViewQuery -{ -public: - typedef std::list filterList_t; - typedef filterList_t::iterator filterList_iter_t; - typedef filterList_t::const_iterator filterList_const_iter_t; - - LLViewQuery() : mPreFilters(), mPostFilters(), mSorterp() {} - virtual ~LLViewQuery() {} - - void addPreFilter(const LLQueryFilter* prefilter) { mPreFilters.push_back(prefilter); } - void addPostFilter(const LLQueryFilter* postfilter) { mPostFilters.push_back(postfilter); } - const filterList_t & getPreFilters() const { return mPreFilters; } - const filterList_t & getPostFilters() const { return mPostFilters; } - - void setSorter(const LLQuerySorter* sorter) { mSorterp = sorter; } - const LLQuerySorter* getSorter() const { return mSorterp; } - - viewList_t run(LLView * view) const; - // syntactic sugar - viewList_t operator () (LLView * view) const { return run(view); } - - // override this method to provide iteration over other types of children - virtual void filterChildren(LLView * view, viewList_t& filtered_children) const; - -private: - - filterResult_t runFilters(LLView * view, const viewList_t children, const filterList_t filters) const; - - filterList_t mPreFilters; - filterList_t mPostFilters; - const LLQuerySorter* mSorterp; -}; - - -#endif // LL_LLVIEWQUERY_H +/** + * @file llviewquery.h + * @brief Query algorithm for flattening and filtering the view hierarchy. + * + * $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$ + */ + +#ifndef LL_LLVIEWQUERY_H +#define LL_LLVIEWQUERY_H + +#include + +#include "llsingleton.h" +#include "llui.h" + +class LLView; + +typedef std::list viewList_t; +typedef std::pair filterResult_t; + +// Abstract base class for all query filters. +class LLQueryFilter +{ +public: + virtual ~LLQueryFilter() {}; + virtual filterResult_t operator() (const LLView* const view, const viewList_t & children) const = 0; +}; + +class LLQuerySorter +{ +public: + virtual ~LLQuerySorter() {}; + virtual void sort(LLView * parent, viewList_t &children) const; +}; + +class LLLeavesFilter : public LLQueryFilter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(LLLeavesFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; +}; + +class LLRootsFilter : public LLQueryFilter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(LLRootsFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; +}; + +class LLVisibleFilter : public LLQueryFilter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(LLVisibleFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; +}; + +class LLEnabledFilter : public LLQueryFilter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(LLEnabledFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; +}; + +class LLTabStopFilter : public LLQueryFilter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(LLTabStopFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; +}; + +class LLCtrlFilter : public LLQueryFilter, public LLSingleton +{ + LLSINGLETON_EMPTY_CTOR(LLCtrlFilter); + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override; +}; + +template +class LLWidgetTypeFilter : public LLQueryFilter +{ + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const + { + return filterResult_t(dynamic_cast(view) != NULL, true); + } + +}; + +// Algorithm for flattening +class LLViewQuery +{ +public: + typedef std::list filterList_t; + typedef filterList_t::iterator filterList_iter_t; + typedef filterList_t::const_iterator filterList_const_iter_t; + + LLViewQuery() : mPreFilters(), mPostFilters(), mSorterp() {} + virtual ~LLViewQuery() {} + + void addPreFilter(const LLQueryFilter* prefilter) { mPreFilters.push_back(prefilter); } + void addPostFilter(const LLQueryFilter* postfilter) { mPostFilters.push_back(postfilter); } + const filterList_t & getPreFilters() const { return mPreFilters; } + const filterList_t & getPostFilters() const { return mPostFilters; } + + void setSorter(const LLQuerySorter* sorter) { mSorterp = sorter; } + const LLQuerySorter* getSorter() const { return mSorterp; } + + viewList_t run(LLView * view) const; + // syntactic sugar + viewList_t operator () (LLView * view) const { return run(view); } + + // override this method to provide iteration over other types of children + virtual void filterChildren(LLView * view, viewList_t& filtered_children) const; + +private: + + filterResult_t runFilters(LLView * view, const viewList_t children, const filterList_t filters) const; + + filterList_t mPreFilters; + filterList_t mPostFilters; + const LLQuerySorter* mSorterp; +}; + + +#endif // LL_LLVIEWQUERY_H diff --git a/indra/llui/llvirtualtrackball.cpp b/indra/llui/llvirtualtrackball.cpp index 78651ff3cb..8166afc89b 100644 --- a/indra/llui/llvirtualtrackball.cpp +++ b/indra/llui/llvirtualtrackball.cpp @@ -1,520 +1,520 @@ -/** -* @file LLVirtualTrackball.cpp -* @author Andrey Lihatskiy -* @brief Implementation for LLVirtualTrackball -* -* $LicenseInfo:firstyear=2001&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2018, 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$ -*/ - -// A control for positioning the sun and the moon in the celestial sphere. - -#include "linden_common.h" -#include "llvirtualtrackball.h" -#include "llstring.h" -#include "llrect.h" -#include "lluictrlfactory.h" -#include "llrender.h" - -// Globals -static LLDefaultChildRegistry::Register register_virtual_trackball("sun_moon_trackball"); - -const LLVector3 VectorZero(1.0f, 0.0f, 0.0f); - -LLVirtualTrackball::Params::Params() - : border("border"), - image_moon_back("image_moon_back"), - image_moon_front("image_moon_front"), - image_sphere("image_sphere"), - image_sun_back("image_sun_back"), - image_sun_front("image_sun_front"), - btn_rotate_top("button_rotate_top"), - btn_rotate_bottom("button_rotate_bottom"), - btn_rotate_left("button_rotate_left"), - btn_rotate_right("button_rotate_right"), - thumb_mode("thumb_mode"), - lbl_N("labelN"), - lbl_S("labelS"), - lbl_W("labelW"), - lbl_E("labelE"), - increment_angle_mouse("increment_angle_mouse", 0.5f), - increment_angle_btn("increment_angle_btn", 3.0f) -{ -} - -LLVirtualTrackball::LLVirtualTrackball(const LLVirtualTrackball::Params& p) - : LLUICtrl(p), - mImgMoonBack(p.image_moon_back), - mImgMoonFront(p.image_moon_front), - mImgSunBack(p.image_sun_back), - mImgSunFront(p.image_sun_front), - mImgSphere(p.image_sphere), - mThumbMode(p.thumb_mode() == "moon" ? ThumbMode::MOON : ThumbMode::SUN), - mIncrementMouse(DEG_TO_RAD * p.increment_angle_mouse()), - mIncrementBtn(DEG_TO_RAD * p.increment_angle_btn()) -{ - LLRect border_rect = getLocalRect(); - S32 centerX = border_rect.getCenterX(); - S32 centerY = border_rect.getCenterY(); - U32 btn_size = 32; // width & height - U32 axis_offset_lt = 16; // offset from the axis for left/top sides - U32 axis_offset_rb = btn_size - axis_offset_lt; // and for right/bottom - - LLViewBorder::Params border = p.border; - border.rect(border_rect); - mBorder = LLUICtrlFactory::create(border); - addChild(mBorder); - - - LLButton::Params btn_rt = p.btn_rotate_top; - btn_rt.rect(LLRect(centerX - axis_offset_lt, border_rect.mTop, centerX + axis_offset_rb, border_rect.mTop - btn_size)); - btn_rt.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this)); - btn_rt.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this)); - btn_rt.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopMouseEnter, this)); - mBtnRotateTop = LLUICtrlFactory::create(btn_rt); - addChild(mBtnRotateTop); - - LLTextBox::Params lbl_N = p.lbl_N; - LLRect rect_N = btn_rt.rect; - //rect_N.translate(btn_rt.rect().getWidth(), 0); - lbl_N.rect = rect_N; - lbl_N.initial_value(lbl_N.label()); - mLabelN = LLUICtrlFactory::create(lbl_N); - addChild(mLabelN); - - - LLButton::Params btn_rr = p.btn_rotate_right; - btn_rr.rect(LLRect(border_rect.mRight - btn_size, centerY + axis_offset_lt, border_rect.mRight, centerY - axis_offset_rb)); - btn_rr.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this)); - btn_rr.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this)); - btn_rr.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightMouseEnter, this)); - mBtnRotateRight = LLUICtrlFactory::create(btn_rr); - addChild(mBtnRotateRight); - - LLTextBox::Params lbl_E = p.lbl_E; - LLRect rect_E = btn_rr.rect; - //rect_E.translate(0, -1 * btn_rr.rect().getHeight()); - lbl_E.rect = rect_E; - lbl_E.initial_value(lbl_E.label()); - mLabelE = LLUICtrlFactory::create(lbl_E); - addChild(mLabelE); - - - LLButton::Params btn_rb = p.btn_rotate_bottom; - btn_rb.rect(LLRect(centerX - axis_offset_lt, border_rect.mBottom + btn_size, centerX + axis_offset_rb, border_rect.mBottom)); - btn_rb.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this)); - btn_rb.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this)); - btn_rb.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomMouseEnter, this)); - mBtnRotateBottom = LLUICtrlFactory::create(btn_rb); - addChild(mBtnRotateBottom); - - LLTextBox::Params lbl_S = p.lbl_S; - LLRect rect_S = btn_rb.rect; - //rect_S.translate(btn_rb.rect().getWidth(), 0); - lbl_S.rect = rect_S; - lbl_S.initial_value(lbl_S.label()); - mLabelS = LLUICtrlFactory::create(lbl_S); - addChild(mLabelS); - - - LLButton::Params btn_rl = p.btn_rotate_left; - btn_rl.rect(LLRect(border_rect.mLeft, centerY + axis_offset_lt, border_rect.mLeft + btn_size, centerY - axis_offset_rb)); - btn_rl.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this)); - btn_rl.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this)); - btn_rl.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftMouseEnter, this)); - mBtnRotateLeft = LLUICtrlFactory::create(btn_rl); - addChild(mBtnRotateLeft); - - LLTextBox::Params lbl_W = p.lbl_W; - LLRect rect_W = btn_rl.rect; - //rect_W.translate(0, -1* btn_rl.rect().getHeight()); - lbl_W.rect = rect_W; - lbl_W.initial_value(lbl_W.label()); - mLabelW = LLUICtrlFactory::create(lbl_W); - addChild(mLabelW); - - - LLPanel::Params touch_area; - touch_area.rect = LLRect(centerX - mImgSphere->getWidth() / 2, - centerY + mImgSphere->getHeight() / 2, - centerX + mImgSphere->getWidth() / 2, - centerY - mImgSphere->getHeight() / 2); - mTouchArea = LLUICtrlFactory::create(touch_area); - addChild(mTouchArea); -} - -LLVirtualTrackball::~LLVirtualTrackball() -{ -} - -bool LLVirtualTrackball::postBuild() -{ - return true; -} - - -void LLVirtualTrackball::drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi) -{ - LLUIImage* thumb; - if (mode == ThumbMode::SUN) - { - if (upperHemi) - { - thumb = mImgSunFront; - } - else - { - thumb = mImgSunBack; - } - } - else - { - if (upperHemi) - { - thumb = mImgMoonFront; - } - else - { - thumb = mImgMoonBack; - } - } - thumb->draw(LLRect(x - thumb->getWidth() / 2, - y + thumb->getHeight() / 2, - x + thumb->getWidth() / 2, - y - thumb->getHeight() / 2)); -} - -bool LLVirtualTrackball::pointInTouchCircle(S32 x, S32 y) const -{ - S32 centerX = mTouchArea->getRect().getCenterX(); - S32 centerY = mTouchArea->getRect().getCenterY(); - - bool in_circle = pow(x - centerX, 2) + pow(y - centerY, 2) <= pow(mTouchArea->getRect().getWidth() / 2, 2); - return in_circle; -} - -void LLVirtualTrackball::draw() -{ - LLVector3 draw_point = VectorZero * mValue; - - S32 halfwidth = mTouchArea->getRect().getWidth() / 2; - S32 halfheight = mTouchArea->getRect().getHeight() / 2; - draw_point.mV[VX] = (draw_point.mV[VX] + 1.0) * halfwidth + mTouchArea->getRect().mLeft; - draw_point.mV[VY] = (draw_point.mV[VY] + 1.0) * halfheight + mTouchArea->getRect().mBottom; - bool upper_hemisphere = (draw_point.mV[VZ] >= 0.f); - - mImgSphere->draw(mTouchArea->getRect(), upper_hemisphere ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f); - drawThumb(draw_point.mV[VX], draw_point.mV[VY], mThumbMode, upper_hemisphere); - - - if (LLView::sDebugRects) - { - gGL.color4fv(LLColor4::red.mV); - gl_circle_2d(mTouchArea->getRect().getCenterX(), mTouchArea->getRect().getCenterY(), mImgSphere->getWidth() / 2, 60, false); - gl_circle_2d(draw_point.mV[VX], draw_point.mV[VY], mImgSunFront->getWidth() / 2, 12, false); - } - - // hide the direction labels when disabled - bool enabled = isInEnabledChain(); - mLabelN->setVisible(enabled); - mLabelE->setVisible(enabled); - mLabelS->setVisible(enabled); - mLabelW->setVisible(enabled); - - LLView::draw(); -} - -void LLVirtualTrackball::onRotateTopClick() -{ - if (getEnabled()) - { - LLQuaternion delta; - delta.setAngleAxis(mIncrementBtn, 1, 0, 0); - mValue *= delta; - setValueAndCommit(mValue); - - make_ui_sound("UISndClick"); - } -} - -void LLVirtualTrackball::onRotateBottomClick() -{ - if (getEnabled()) - { - LLQuaternion delta; - delta.setAngleAxis(mIncrementBtn, -1, 0, 0); - mValue *= delta; - setValueAndCommit(mValue); - - make_ui_sound("UISndClick"); - } -} - -void LLVirtualTrackball::onRotateLeftClick() -{ - if (getEnabled()) - { - LLQuaternion delta; - delta.setAngleAxis(mIncrementBtn, 0, 1, 0); - mValue *= delta; - setValueAndCommit(mValue); - - make_ui_sound("UISndClick"); - } -} - -void LLVirtualTrackball::onRotateRightClick() -{ - if (getEnabled()) - { - LLQuaternion delta; - delta.setAngleAxis(mIncrementBtn, 0, -1, 0); - mValue *= delta; - setValueAndCommit(mValue); - - make_ui_sound("UISndClick"); - } -} - -void LLVirtualTrackball::onRotateTopMouseEnter() -{ - mBtnRotateTop->setHighlight(true); -} - -void LLVirtualTrackball::onRotateBottomMouseEnter() -{ - mBtnRotateBottom->setHighlight(true); -} - -void LLVirtualTrackball::onRotateLeftMouseEnter() -{ - mBtnRotateLeft->setHighlight(true); -} - -void LLVirtualTrackball::onRotateRightMouseEnter() -{ - mBtnRotateRight->setHighlight(true); -} - -void LLVirtualTrackball::setValue(const LLSD& value) -{ - if (value.isArray() && value.size() == 4) - { - mValue.setValue(value); - } -} - -void LLVirtualTrackball::setRotation(const LLQuaternion &value) -{ - mValue = value; -} - -void LLVirtualTrackball::setValue(F32 x, F32 y, F32 z, F32 w) -{ - mValue.set(x, y, z, w); -} - -void LLVirtualTrackball::setValueAndCommit(const LLQuaternion &value) -{ - mValue = value; - onCommit(); -} - -LLSD LLVirtualTrackball::getValue() const -{ - return mValue.getValue(); -} - -LLQuaternion LLVirtualTrackball::getRotation() const -{ - return mValue; -} - -// static -void LLVirtualTrackball::getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation) -{ - // LLQuaternion has own function to get azimuth, but it doesn't appear to return correct values (meant for 2d?) - LLVector3 point = VectorZero * quat; - - if (!is_approx_zero(point.mV[VX]) || !is_approx_zero(point.mV[VY])) - { - azimuth = atan2f(point.mV[VX], point.mV[VY]); - } - else - { - azimuth = 0; - } - - azimuth -= F_PI_BY_TWO; - - if (azimuth < 0) - { - azimuth += F_PI * 2; - } - - // while vector is '1', F32 is not sufficiently precise and we can get - // values like 1.0000012 which will result in -90deg angle instead of 90deg - F32 z = llclamp(point.mV[VZ], -1.f, 1.f); - elevation = asin(z); // because VectorZero's length is '1' -} - -// static -void LLVirtualTrackball::getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation) -{ - getAzimuthAndElevation(quat, azimuth, elevation); - azimuth *= RAD_TO_DEG; - elevation *= RAD_TO_DEG; -} - -bool LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask) -{ - if (hasMouseCapture()) - { - if (mDragMode == DRAG_SCROLL) - { // trackball (move to roll) mode - LLQuaternion delta; - - F32 rotX = x - mPrevX; - F32 rotY = y - mPrevY; - - if (abs(rotX) > 1) - { - F32 direction = (rotX < 0) ? -1 : 1; - delta.setAngleAxis(mIncrementMouse * abs(rotX), 0, direction, 0); // changing X - rotate around Y axis - mValue *= delta; - } - - if (abs(rotY) > 1) - { - F32 direction = (rotY < 0) ? 1 : -1; // reverse for Y (value increases from bottom to top) - delta.setAngleAxis(mIncrementMouse * abs(rotY), direction, 0, 0); // changing Y - rotate around X axis - mValue *= delta; - } - } - else - { // set on click mode - if (!pointInTouchCircle(x, y)) - { - return true; // don't drag outside the circle - } - - F32 radius = mTouchArea->getRect().getWidth() / 2; - F32 xx = x - mTouchArea->getRect().getCenterX(); - F32 yy = y - mTouchArea->getRect().getCenterY(); - F32 dist = sqrt(pow(xx, 2) + pow(yy, 2)); - - F32 azimuth = llclamp(acosf(xx / dist), 0.0f, F_PI); - F32 altitude = llclamp(acosf(dist / radius), 0.0f, F_PI_BY_TWO); - - if (yy < 0) - { - azimuth = F_TWO_PI - azimuth; - } - - LLVector3 draw_point = VectorZero * mValue; - if (draw_point.mV[VZ] >= 0.f) - { - if (is_approx_zero(altitude)) // don't change the hemisphere - { - altitude = F_APPROXIMATELY_ZERO; - } - altitude *= -1; - } - - mValue.setAngleAxis(altitude, 0, 1, 0); - LLQuaternion az_quat; - az_quat.setAngleAxis(azimuth, 0, 0, 1); - mValue *= az_quat; - } - - // we are doing a lot of F32 mathematical operations with loss of precision, - // re-normalize to compensate - mValue.normalize(); - - mPrevX = x; - mPrevY = y; - onCommit(); - } - return true; -} - -bool LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if (hasMouseCapture()) - { - mPrevX = 0; - mPrevY = 0; - gFocusMgr.setMouseCapture(NULL); - make_ui_sound("UISndClickRelease"); - } - return LLView::handleMouseUp(x, y, mask); -} - -bool LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (pointInTouchCircle(x, y)) - { - mPrevX = x; - mPrevY = y; - gFocusMgr.setMouseCapture(this); - mDragMode = (mask == MASK_CONTROL) ? DRAG_SCROLL : DRAG_SET; - make_ui_sound("UISndClick"); - } - return LLView::handleMouseDown(x, y, mask); -} - -bool LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - if (pointInTouchCircle(x, y)) - { - - //make_ui_sound("UISndClick"); - } - return LLView::handleRightMouseDown(x, y, mask); -} - -bool LLVirtualTrackball::handleKeyHere(KEY key, MASK mask) -{ - bool handled = false; - switch (key) - { - case KEY_DOWN: - onRotateTopClick(); - handled = true; - break; - case KEY_LEFT: - onRotateRightClick(); - handled = true; - break; - case KEY_UP: - onRotateBottomClick(); - handled = true; - break; - case KEY_RIGHT: - onRotateLeftClick(); - handled = true; - break; - default: - break; - } - return handled; -} - +/** +* @file LLVirtualTrackball.cpp +* @author Andrey Lihatskiy +* @brief Implementation for LLVirtualTrackball +* +* $LicenseInfo:firstyear=2001&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, 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$ +*/ + +// A control for positioning the sun and the moon in the celestial sphere. + +#include "linden_common.h" +#include "llvirtualtrackball.h" +#include "llstring.h" +#include "llrect.h" +#include "lluictrlfactory.h" +#include "llrender.h" + +// Globals +static LLDefaultChildRegistry::Register register_virtual_trackball("sun_moon_trackball"); + +const LLVector3 VectorZero(1.0f, 0.0f, 0.0f); + +LLVirtualTrackball::Params::Params() + : border("border"), + image_moon_back("image_moon_back"), + image_moon_front("image_moon_front"), + image_sphere("image_sphere"), + image_sun_back("image_sun_back"), + image_sun_front("image_sun_front"), + btn_rotate_top("button_rotate_top"), + btn_rotate_bottom("button_rotate_bottom"), + btn_rotate_left("button_rotate_left"), + btn_rotate_right("button_rotate_right"), + thumb_mode("thumb_mode"), + lbl_N("labelN"), + lbl_S("labelS"), + lbl_W("labelW"), + lbl_E("labelE"), + increment_angle_mouse("increment_angle_mouse", 0.5f), + increment_angle_btn("increment_angle_btn", 3.0f) +{ +} + +LLVirtualTrackball::LLVirtualTrackball(const LLVirtualTrackball::Params& p) + : LLUICtrl(p), + mImgMoonBack(p.image_moon_back), + mImgMoonFront(p.image_moon_front), + mImgSunBack(p.image_sun_back), + mImgSunFront(p.image_sun_front), + mImgSphere(p.image_sphere), + mThumbMode(p.thumb_mode() == "moon" ? ThumbMode::MOON : ThumbMode::SUN), + mIncrementMouse(DEG_TO_RAD * p.increment_angle_mouse()), + mIncrementBtn(DEG_TO_RAD * p.increment_angle_btn()) +{ + LLRect border_rect = getLocalRect(); + S32 centerX = border_rect.getCenterX(); + S32 centerY = border_rect.getCenterY(); + U32 btn_size = 32; // width & height + U32 axis_offset_lt = 16; // offset from the axis for left/top sides + U32 axis_offset_rb = btn_size - axis_offset_lt; // and for right/bottom + + LLViewBorder::Params border = p.border; + border.rect(border_rect); + mBorder = LLUICtrlFactory::create(border); + addChild(mBorder); + + + LLButton::Params btn_rt = p.btn_rotate_top; + btn_rt.rect(LLRect(centerX - axis_offset_lt, border_rect.mTop, centerX + axis_offset_rb, border_rect.mTop - btn_size)); + btn_rt.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this)); + btn_rt.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this)); + btn_rt.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopMouseEnter, this)); + mBtnRotateTop = LLUICtrlFactory::create(btn_rt); + addChild(mBtnRotateTop); + + LLTextBox::Params lbl_N = p.lbl_N; + LLRect rect_N = btn_rt.rect; + //rect_N.translate(btn_rt.rect().getWidth(), 0); + lbl_N.rect = rect_N; + lbl_N.initial_value(lbl_N.label()); + mLabelN = LLUICtrlFactory::create(lbl_N); + addChild(mLabelN); + + + LLButton::Params btn_rr = p.btn_rotate_right; + btn_rr.rect(LLRect(border_rect.mRight - btn_size, centerY + axis_offset_lt, border_rect.mRight, centerY - axis_offset_rb)); + btn_rr.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this)); + btn_rr.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this)); + btn_rr.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightMouseEnter, this)); + mBtnRotateRight = LLUICtrlFactory::create(btn_rr); + addChild(mBtnRotateRight); + + LLTextBox::Params lbl_E = p.lbl_E; + LLRect rect_E = btn_rr.rect; + //rect_E.translate(0, -1 * btn_rr.rect().getHeight()); + lbl_E.rect = rect_E; + lbl_E.initial_value(lbl_E.label()); + mLabelE = LLUICtrlFactory::create(lbl_E); + addChild(mLabelE); + + + LLButton::Params btn_rb = p.btn_rotate_bottom; + btn_rb.rect(LLRect(centerX - axis_offset_lt, border_rect.mBottom + btn_size, centerX + axis_offset_rb, border_rect.mBottom)); + btn_rb.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this)); + btn_rb.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this)); + btn_rb.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomMouseEnter, this)); + mBtnRotateBottom = LLUICtrlFactory::create(btn_rb); + addChild(mBtnRotateBottom); + + LLTextBox::Params lbl_S = p.lbl_S; + LLRect rect_S = btn_rb.rect; + //rect_S.translate(btn_rb.rect().getWidth(), 0); + lbl_S.rect = rect_S; + lbl_S.initial_value(lbl_S.label()); + mLabelS = LLUICtrlFactory::create(lbl_S); + addChild(mLabelS); + + + LLButton::Params btn_rl = p.btn_rotate_left; + btn_rl.rect(LLRect(border_rect.mLeft, centerY + axis_offset_lt, border_rect.mLeft + btn_size, centerY - axis_offset_rb)); + btn_rl.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this)); + btn_rl.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this)); + btn_rl.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftMouseEnter, this)); + mBtnRotateLeft = LLUICtrlFactory::create(btn_rl); + addChild(mBtnRotateLeft); + + LLTextBox::Params lbl_W = p.lbl_W; + LLRect rect_W = btn_rl.rect; + //rect_W.translate(0, -1* btn_rl.rect().getHeight()); + lbl_W.rect = rect_W; + lbl_W.initial_value(lbl_W.label()); + mLabelW = LLUICtrlFactory::create(lbl_W); + addChild(mLabelW); + + + LLPanel::Params touch_area; + touch_area.rect = LLRect(centerX - mImgSphere->getWidth() / 2, + centerY + mImgSphere->getHeight() / 2, + centerX + mImgSphere->getWidth() / 2, + centerY - mImgSphere->getHeight() / 2); + mTouchArea = LLUICtrlFactory::create(touch_area); + addChild(mTouchArea); +} + +LLVirtualTrackball::~LLVirtualTrackball() +{ +} + +bool LLVirtualTrackball::postBuild() +{ + return true; +} + + +void LLVirtualTrackball::drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi) +{ + LLUIImage* thumb; + if (mode == ThumbMode::SUN) + { + if (upperHemi) + { + thumb = mImgSunFront; + } + else + { + thumb = mImgSunBack; + } + } + else + { + if (upperHemi) + { + thumb = mImgMoonFront; + } + else + { + thumb = mImgMoonBack; + } + } + thumb->draw(LLRect(x - thumb->getWidth() / 2, + y + thumb->getHeight() / 2, + x + thumb->getWidth() / 2, + y - thumb->getHeight() / 2)); +} + +bool LLVirtualTrackball::pointInTouchCircle(S32 x, S32 y) const +{ + S32 centerX = mTouchArea->getRect().getCenterX(); + S32 centerY = mTouchArea->getRect().getCenterY(); + + bool in_circle = pow(x - centerX, 2) + pow(y - centerY, 2) <= pow(mTouchArea->getRect().getWidth() / 2, 2); + return in_circle; +} + +void LLVirtualTrackball::draw() +{ + LLVector3 draw_point = VectorZero * mValue; + + S32 halfwidth = mTouchArea->getRect().getWidth() / 2; + S32 halfheight = mTouchArea->getRect().getHeight() / 2; + draw_point.mV[VX] = (draw_point.mV[VX] + 1.0) * halfwidth + mTouchArea->getRect().mLeft; + draw_point.mV[VY] = (draw_point.mV[VY] + 1.0) * halfheight + mTouchArea->getRect().mBottom; + bool upper_hemisphere = (draw_point.mV[VZ] >= 0.f); + + mImgSphere->draw(mTouchArea->getRect(), upper_hemisphere ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f); + drawThumb(draw_point.mV[VX], draw_point.mV[VY], mThumbMode, upper_hemisphere); + + + if (LLView::sDebugRects) + { + gGL.color4fv(LLColor4::red.mV); + gl_circle_2d(mTouchArea->getRect().getCenterX(), mTouchArea->getRect().getCenterY(), mImgSphere->getWidth() / 2, 60, false); + gl_circle_2d(draw_point.mV[VX], draw_point.mV[VY], mImgSunFront->getWidth() / 2, 12, false); + } + + // hide the direction labels when disabled + bool enabled = isInEnabledChain(); + mLabelN->setVisible(enabled); + mLabelE->setVisible(enabled); + mLabelS->setVisible(enabled); + mLabelW->setVisible(enabled); + + LLView::draw(); +} + +void LLVirtualTrackball::onRotateTopClick() +{ + if (getEnabled()) + { + LLQuaternion delta; + delta.setAngleAxis(mIncrementBtn, 1, 0, 0); + mValue *= delta; + setValueAndCommit(mValue); + + make_ui_sound("UISndClick"); + } +} + +void LLVirtualTrackball::onRotateBottomClick() +{ + if (getEnabled()) + { + LLQuaternion delta; + delta.setAngleAxis(mIncrementBtn, -1, 0, 0); + mValue *= delta; + setValueAndCommit(mValue); + + make_ui_sound("UISndClick"); + } +} + +void LLVirtualTrackball::onRotateLeftClick() +{ + if (getEnabled()) + { + LLQuaternion delta; + delta.setAngleAxis(mIncrementBtn, 0, 1, 0); + mValue *= delta; + setValueAndCommit(mValue); + + make_ui_sound("UISndClick"); + } +} + +void LLVirtualTrackball::onRotateRightClick() +{ + if (getEnabled()) + { + LLQuaternion delta; + delta.setAngleAxis(mIncrementBtn, 0, -1, 0); + mValue *= delta; + setValueAndCommit(mValue); + + make_ui_sound("UISndClick"); + } +} + +void LLVirtualTrackball::onRotateTopMouseEnter() +{ + mBtnRotateTop->setHighlight(true); +} + +void LLVirtualTrackball::onRotateBottomMouseEnter() +{ + mBtnRotateBottom->setHighlight(true); +} + +void LLVirtualTrackball::onRotateLeftMouseEnter() +{ + mBtnRotateLeft->setHighlight(true); +} + +void LLVirtualTrackball::onRotateRightMouseEnter() +{ + mBtnRotateRight->setHighlight(true); +} + +void LLVirtualTrackball::setValue(const LLSD& value) +{ + if (value.isArray() && value.size() == 4) + { + mValue.setValue(value); + } +} + +void LLVirtualTrackball::setRotation(const LLQuaternion &value) +{ + mValue = value; +} + +void LLVirtualTrackball::setValue(F32 x, F32 y, F32 z, F32 w) +{ + mValue.set(x, y, z, w); +} + +void LLVirtualTrackball::setValueAndCommit(const LLQuaternion &value) +{ + mValue = value; + onCommit(); +} + +LLSD LLVirtualTrackball::getValue() const +{ + return mValue.getValue(); +} + +LLQuaternion LLVirtualTrackball::getRotation() const +{ + return mValue; +} + +// static +void LLVirtualTrackball::getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation) +{ + // LLQuaternion has own function to get azimuth, but it doesn't appear to return correct values (meant for 2d?) + LLVector3 point = VectorZero * quat; + + if (!is_approx_zero(point.mV[VX]) || !is_approx_zero(point.mV[VY])) + { + azimuth = atan2f(point.mV[VX], point.mV[VY]); + } + else + { + azimuth = 0; + } + + azimuth -= F_PI_BY_TWO; + + if (azimuth < 0) + { + azimuth += F_PI * 2; + } + + // while vector is '1', F32 is not sufficiently precise and we can get + // values like 1.0000012 which will result in -90deg angle instead of 90deg + F32 z = llclamp(point.mV[VZ], -1.f, 1.f); + elevation = asin(z); // because VectorZero's length is '1' +} + +// static +void LLVirtualTrackball::getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation) +{ + getAzimuthAndElevation(quat, azimuth, elevation); + azimuth *= RAD_TO_DEG; + elevation *= RAD_TO_DEG; +} + +bool LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask) +{ + if (hasMouseCapture()) + { + if (mDragMode == DRAG_SCROLL) + { // trackball (move to roll) mode + LLQuaternion delta; + + F32 rotX = x - mPrevX; + F32 rotY = y - mPrevY; + + if (abs(rotX) > 1) + { + F32 direction = (rotX < 0) ? -1 : 1; + delta.setAngleAxis(mIncrementMouse * abs(rotX), 0, direction, 0); // changing X - rotate around Y axis + mValue *= delta; + } + + if (abs(rotY) > 1) + { + F32 direction = (rotY < 0) ? 1 : -1; // reverse for Y (value increases from bottom to top) + delta.setAngleAxis(mIncrementMouse * abs(rotY), direction, 0, 0); // changing Y - rotate around X axis + mValue *= delta; + } + } + else + { // set on click mode + if (!pointInTouchCircle(x, y)) + { + return true; // don't drag outside the circle + } + + F32 radius = mTouchArea->getRect().getWidth() / 2; + F32 xx = x - mTouchArea->getRect().getCenterX(); + F32 yy = y - mTouchArea->getRect().getCenterY(); + F32 dist = sqrt(pow(xx, 2) + pow(yy, 2)); + + F32 azimuth = llclamp(acosf(xx / dist), 0.0f, F_PI); + F32 altitude = llclamp(acosf(dist / radius), 0.0f, F_PI_BY_TWO); + + if (yy < 0) + { + azimuth = F_TWO_PI - azimuth; + } + + LLVector3 draw_point = VectorZero * mValue; + if (draw_point.mV[VZ] >= 0.f) + { + if (is_approx_zero(altitude)) // don't change the hemisphere + { + altitude = F_APPROXIMATELY_ZERO; + } + altitude *= -1; + } + + mValue.setAngleAxis(altitude, 0, 1, 0); + LLQuaternion az_quat; + az_quat.setAngleAxis(azimuth, 0, 0, 1); + mValue *= az_quat; + } + + // we are doing a lot of F32 mathematical operations with loss of precision, + // re-normalize to compensate + mValue.normalize(); + + mPrevX = x; + mPrevY = y; + onCommit(); + } + return true; +} + +bool LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (hasMouseCapture()) + { + mPrevX = 0; + mPrevY = 0; + gFocusMgr.setMouseCapture(NULL); + make_ui_sound("UISndClickRelease"); + } + return LLView::handleMouseUp(x, y, mask); +} + +bool LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (pointInTouchCircle(x, y)) + { + mPrevX = x; + mPrevY = y; + gFocusMgr.setMouseCapture(this); + mDragMode = (mask == MASK_CONTROL) ? DRAG_SCROLL : DRAG_SET; + make_ui_sound("UISndClick"); + } + return LLView::handleMouseDown(x, y, mask); +} + +bool LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (pointInTouchCircle(x, y)) + { + + //make_ui_sound("UISndClick"); + } + return LLView::handleRightMouseDown(x, y, mask); +} + +bool LLVirtualTrackball::handleKeyHere(KEY key, MASK mask) +{ + bool handled = false; + switch (key) + { + case KEY_DOWN: + onRotateTopClick(); + handled = true; + break; + case KEY_LEFT: + onRotateRightClick(); + handled = true; + break; + case KEY_UP: + onRotateBottomClick(); + handled = true; + break; + case KEY_RIGHT: + onRotateLeftClick(); + handled = true; + break; + default: + break; + } + return handled; +} + diff --git a/indra/llui/llvirtualtrackball.h b/indra/llui/llvirtualtrackball.h index 54ed6082ea..61a78b2398 100644 --- a/indra/llui/llvirtualtrackball.h +++ b/indra/llui/llvirtualtrackball.h @@ -1,163 +1,163 @@ -/** -* @file virtualtrackball.h -* @author Andrey Lihatskiy -* @brief Header file for LLVirtualTrackball -* -* $LicenseInfo:firstyear=2001&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2018, 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$ -*/ - -// A control for positioning the sun and the moon in the celestial sphere. - -#ifndef LL_LLVIRTUALTRACKBALL_H -#define LL_LLVIRTUALTRACKBALL_H - -#include "lluictrl.h" -#include "llpanel.h" -#include "lltextbox.h" -#include "llbutton.h" - -class LLVirtualTrackball - : public LLUICtrl -{ -public: - enum ThumbMode - { - SUN, - MOON - }; - enum DragMode - { - DRAG_SET, - DRAG_SCROLL - }; - - struct Params - : public LLInitParam::Block - { - Optional border; - Optional image_moon_back, - image_moon_front, - image_sphere, - image_sun_back, - image_sun_front; - - Optional thumb_mode; - Optional increment_angle_mouse, - increment_angle_btn; - - Optional lbl_N, - lbl_S, - lbl_W, - lbl_E; - - Optional btn_rotate_top, - btn_rotate_bottom, - btn_rotate_left, - btn_rotate_right; - - Params(); - }; - - - virtual ~LLVirtualTrackball(); - /*virtual*/ bool postBuild(); - - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual bool handleKeyHere(KEY key, MASK mask); - - virtual void draw(); - - virtual void setValue(const LLSD& value); - void setValue(F32 x, F32 y, F32 z, F32 w); - virtual LLSD getValue() const; - - void setRotation(const LLQuaternion &value); - LLQuaternion getRotation() const; - - static void getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation); - static void getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation); - -protected: - friend class LLUICtrlFactory; - LLVirtualTrackball(const Params&); - void onEditChange(); - -protected: - LLTextBox* mNLabel; - LLTextBox* mELabel; - LLTextBox* mSLabel; - LLTextBox* mWLabel; - - LLButton* mBtnRotateTop; - LLButton* mBtnRotateBottom; - LLButton* mBtnRotateLeft; - LLButton* mBtnRotateRight; - - LLTextBox* mLabelN; - LLTextBox* mLabelS; - LLTextBox* mLabelW; - LLTextBox* mLabelE; - - LLPanel* mTouchArea; - LLViewBorder* mBorder; - -private: - void setValueAndCommit(const LLQuaternion &value); - void drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi = true); - bool pointInTouchCircle(S32 x, S32 y) const; - - void onRotateTopClick(); - void onRotateBottomClick(); - void onRotateLeftClick(); - void onRotateRightClick(); - - void onRotateTopMouseEnter(); - void onRotateBottomMouseEnter(); - void onRotateLeftMouseEnter(); - void onRotateRightMouseEnter(); - - S32 mPrevX; - S32 mPrevY; - - LLUIImage* mImgMoonBack; - LLUIImage* mImgMoonFront; - LLUIImage* mImgSunBack; - LLUIImage* mImgSunFront; - LLUIImage* mImgBtnRotTop; - LLUIImage* mImgBtnRotLeft; - LLUIImage* mImgBtnRotRight; - LLUIImage* mImgBtnRotBottom; - LLUIImage* mImgSphere; - - LLQuaternion mValue; - ThumbMode mThumbMode; - DragMode mDragMode; - - F32 mIncrementMouse; - F32 mIncrementBtn; -}; - -#endif - +/** +* @file virtualtrackball.h +* @author Andrey Lihatskiy +* @brief Header file for LLVirtualTrackball +* +* $LicenseInfo:firstyear=2001&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, 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$ +*/ + +// A control for positioning the sun and the moon in the celestial sphere. + +#ifndef LL_LLVIRTUALTRACKBALL_H +#define LL_LLVIRTUALTRACKBALL_H + +#include "lluictrl.h" +#include "llpanel.h" +#include "lltextbox.h" +#include "llbutton.h" + +class LLVirtualTrackball + : public LLUICtrl +{ +public: + enum ThumbMode + { + SUN, + MOON + }; + enum DragMode + { + DRAG_SET, + DRAG_SCROLL + }; + + struct Params + : public LLInitParam::Block + { + Optional border; + Optional image_moon_back, + image_moon_front, + image_sphere, + image_sun_back, + image_sun_front; + + Optional thumb_mode; + Optional increment_angle_mouse, + increment_angle_btn; + + Optional lbl_N, + lbl_S, + lbl_W, + lbl_E; + + Optional btn_rotate_top, + btn_rotate_bottom, + btn_rotate_left, + btn_rotate_right; + + Params(); + }; + + + virtual ~LLVirtualTrackball(); + /*virtual*/ bool postBuild(); + + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); + virtual bool handleKeyHere(KEY key, MASK mask); + + virtual void draw(); + + virtual void setValue(const LLSD& value); + void setValue(F32 x, F32 y, F32 z, F32 w); + virtual LLSD getValue() const; + + void setRotation(const LLQuaternion &value); + LLQuaternion getRotation() const; + + static void getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation); + static void getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation); + +protected: + friend class LLUICtrlFactory; + LLVirtualTrackball(const Params&); + void onEditChange(); + +protected: + LLTextBox* mNLabel; + LLTextBox* mELabel; + LLTextBox* mSLabel; + LLTextBox* mWLabel; + + LLButton* mBtnRotateTop; + LLButton* mBtnRotateBottom; + LLButton* mBtnRotateLeft; + LLButton* mBtnRotateRight; + + LLTextBox* mLabelN; + LLTextBox* mLabelS; + LLTextBox* mLabelW; + LLTextBox* mLabelE; + + LLPanel* mTouchArea; + LLViewBorder* mBorder; + +private: + void setValueAndCommit(const LLQuaternion &value); + void drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi = true); + bool pointInTouchCircle(S32 x, S32 y) const; + + void onRotateTopClick(); + void onRotateBottomClick(); + void onRotateLeftClick(); + void onRotateRightClick(); + + void onRotateTopMouseEnter(); + void onRotateBottomMouseEnter(); + void onRotateLeftMouseEnter(); + void onRotateRightMouseEnter(); + + S32 mPrevX; + S32 mPrevY; + + LLUIImage* mImgMoonBack; + LLUIImage* mImgMoonFront; + LLUIImage* mImgSunBack; + LLUIImage* mImgSunFront; + LLUIImage* mImgBtnRotTop; + LLUIImage* mImgBtnRotLeft; + LLUIImage* mImgBtnRotRight; + LLUIImage* mImgBtnRotBottom; + LLUIImage* mImgSphere; + + LLQuaternion mValue; + ThumbMode mThumbMode; + DragMode mDragMode; + + F32 mIncrementMouse; + F32 mIncrementBtn; +}; + +#endif + diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index bfa91b3729..8fd85a89a1 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -1,1765 +1,1765 @@ -/** - * @file llxuiparser.cpp - * @brief Utility functions for handling XUI structures in XML - * - * $LicenseInfo:firstyear=2003&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 "llxuiparser.h" - -#include "llxmlnode.h" -#include "llfasttimer.h" -#ifdef LL_USESYSTEMLIBS -#include -#else -#include "expat/expat.h" -#endif - -#include -#include -#include -//#include -#include - -#include "lluicolor.h" -#include "v3math.h" -using namespace BOOST_SPIRIT_CLASSIC_NS; - -const S32 MAX_STRING_ATTRIBUTE_SIZE = 40; - -static LLInitParam::Parser::parser_read_func_map_t sXSDReadFuncs; -static LLInitParam::Parser::parser_write_func_map_t sXSDWriteFuncs; -static LLInitParam::Parser::parser_inspect_func_map_t sXSDInspectFuncs; - -static LLInitParam::Parser::parser_read_func_map_t sSimpleXUIReadFuncs; -static LLInitParam::Parser::parser_write_func_map_t sSimpleXUIWriteFuncs; -static LLInitParam::Parser::parser_inspect_func_map_t sSimpleXUIInspectFuncs; - -const char* NO_VALUE_MARKER = "no_value"; - -struct MaxOccursValues : public LLInitParam::TypeValuesHelper -{ - static void declareValues() - { - declare("unbounded", U32_MAX); - } -}; - -struct Occurs : public LLInitParam::Block -{ - Optional minOccurs; - Optional maxOccurs; - - Occurs() - : minOccurs("minOccurs", 0), - maxOccurs("maxOccurs", U32_MAX) - - {} -}; - -typedef enum -{ - USE_REQUIRED, - USE_OPTIONAL -} EUse; - -namespace LLInitParam -{ - template<> - struct TypeValues : public TypeValuesHelper - { - static void declareValues() - { - declare("required", USE_REQUIRED); - declare("optional", USE_OPTIONAL); - } - }; -} - -struct Element; -struct Group; -struct Sequence; - -struct All : public LLInitParam::Block -{ - Multiple< Lazy > elements; - - All() - : elements("element") - { - maxOccurs = 1; - } -}; - -struct Attribute : public LLInitParam::Block -{ - Mandatory name, - type; - Mandatory use; - - Attribute() - : name("name"), - type("type"), - use("use") - {} -}; - -struct Any : public LLInitParam::Block -{ - Optional _namespace; - - Any() - : _namespace("namespace") - {} -}; - -struct Choice : public LLInitParam::ChoiceBlock -{ - Alternative< Lazy > element; - Alternative< Lazy > group; - Alternative< Lazy > choice; - Alternative< Lazy > sequence; - Alternative< Lazy > any; - - Choice() - : element("element"), - group("group"), - choice("choice"), - sequence("sequence"), - any("any") - {} - -}; - -struct Sequence : public LLInitParam::ChoiceBlock -{ - Alternative< Lazy > element; - Alternative< Lazy > group; - Alternative< Lazy > choice; - Alternative< Lazy > sequence; - Alternative< Lazy > any; -}; - -struct GroupContents : public LLInitParam::ChoiceBlock -{ - Alternative all; - Alternative choice; - Alternative sequence; - - GroupContents() - : all("all"), - choice("choice"), - sequence("sequence") - {} -}; - -struct Group : public LLInitParam::Block -{ - Optional name, - ref; - - Group() - : name("name"), - ref("ref") - {} -}; - -struct Restriction : public LLInitParam::Block -{ -}; - -struct Extension : public LLInitParam::Block -{ -}; - -struct SimpleContent : public LLInitParam::ChoiceBlock -{ - Alternative restriction; - Alternative extension; - - SimpleContent() - : restriction("restriction"), - extension("extension") - {} -}; - -struct SimpleType : public LLInitParam::Block -{ - // TODO -}; - -struct ComplexContent : public LLInitParam::Block -{ - Optional mixed; - - ComplexContent() - : mixed("mixed", true) - {} -}; - -struct ComplexTypeContents : public LLInitParam::ChoiceBlock -{ - Alternative simple_content; - Alternative complex_content; - Alternative group; - Alternative all; - Alternative choice; - Alternative sequence; - - ComplexTypeContents() - : simple_content("simpleContent"), - complex_content("complexContent"), - group("group"), - all("all"), - choice("choice"), - sequence("sequence") - {} -}; - -struct ComplexType : public LLInitParam::Block -{ - Optional name; - Optional mixed; - - Multiple attribute; - Multiple< Lazy > elements; - - ComplexType() - : name("name"), - attribute("xs:attribute"), - elements("xs:element"), - mixed("mixed") - { - } -}; - -struct ElementContents : public LLInitParam::ChoiceBlock -{ - Alternative simpleType; - Alternative complexType; - - ElementContents() - : simpleType("simpleType"), - complexType("complexType") - {} -}; - -struct Element : public LLInitParam::Block -{ - Optional name, - ref, - type; - - Element() - : name("xs:name"), - ref("xs:ref"), - type("xs:type") - {} -}; - -struct Schema : public LLInitParam::Block -{ -private: - Mandatory targetNamespace, - xmlns, - xs; - -public: - Optional attributeFormDefault, - elementFormDefault; - - Mandatory root_element; - - void setNameSpace(const std::string& ns) {targetNamespace = ns; xmlns = ns;} - - Schema(const std::string& ns = LLStringUtil::null) - : attributeFormDefault("attributeFormDefault"), - elementFormDefault("elementFormDefault"), - xs("xmlns:xs"), - targetNamespace("targetNamespace"), - xmlns("xmlns"), - root_element("xs:element") - { - attributeFormDefault = "unqualified"; - elementFormDefault = "qualified"; - xs = "http://www.w3.org/2001/XMLSchema"; - if (!ns.empty()) - { - setNameSpace(ns); - }; - } -}; - -// -// LLXSDWriter -// -LLXSDWriter::LLXSDWriter() -: Parser(sXSDReadFuncs, sXSDWriteFuncs, sXSDInspectFuncs) -{ - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); - registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); -} - -LLXSDWriter::~LLXSDWriter() {} - -void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace) -{ - Schema schema(xml_namespace); - - schema.root_element.name = type_name; - Choice& choice = schema.root_element.complexType.choice; - - choice.minOccurs = 0; - choice.maxOccurs = "unbounded"; - - mSchemaNode = node; - //node->setName("xs:schema"); - //node->createChild("attributeFormDefault", true)->setStringValue("unqualified"); - //node->createChild("elementFormDefault", true)->setStringValue("qualified"); - //node->createChild("targetNamespace", true)->setStringValue(xml_namespace); - //node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema"); - //node->createChild("xmlns", true)->setStringValue(xml_namespace); - - //node = node->createChild("xs:complexType", false); - //node->createChild("name", true)->setStringValue(type_name); - //node->createChild("mixed", true)->setStringValue("true"); - - //mAttributeNode = node; - //mElementNode = node->createChild("xs:choice", false); - //mElementNode->createChild("minOccurs", true)->setStringValue("0"); - //mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded"); - block.inspectBlock(*this); - - // duplicate element choices - LLXMLNodeList children; - mElementNode->getChildren("xs:element", children, false); - for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it) - { - LLXMLNodePtr child_copy = child_it->second->deepCopy(); - std::string child_name; - child_copy->getAttributeString("name", child_name); - child_copy->setAttributeString("name", type_name + "." + child_name); - mElementNode->addChild(child_copy); - } - - LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false); - element_declaration_node->createChild("name", true)->setStringValue(type_name); - element_declaration_node->createChild("type", true)->setStringValue(type_name); -} - -void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector* possible_values) -{ - name_stack_t non_empty_names; - std::string attribute_name; - for (name_stack_t::const_iterator it = stack.begin(); - it != stack.end(); - ++it) - { - const std::string& name = it->first; - if (!name.empty()) - { - non_empty_names.push_back(*it); - } - } - - for (name_stack_t::const_iterator it = non_empty_names.begin(); - it != non_empty_names.end(); - ++it) - { - if (!attribute_name.empty()) - { - attribute_name += "."; - } - attribute_name += it->first; - } - - // only flag non-nested attributes as mandatory, nested attributes have variant syntax - // that can't be properly constrained in XSD - // e.g. vs - bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1; - - // don't bother supporting "Multiple" params as xml attributes - if (max_count <= 1) - { - // add compound attribute to root node - addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values); - } - - // now generated nested elements for compound attributes - if (non_empty_names.size() > 1 && !attribute_mandatory) - { - std::string element_name; - - // traverse all but last element, leaving that as an attribute name - name_stack_t::const_iterator end_it = non_empty_names.end(); - end_it--; - - for (name_stack_t::const_iterator it = non_empty_names.begin(); - it != end_it; - ++it) - { - if (it != non_empty_names.begin()) - { - element_name += "."; - } - element_name += it->first; - } - - std::string short_attribute_name = non_empty_names.back().first; - - LLXMLNodePtr complex_type_node; - - // find existing element node here, starting at tail of child list - if (mElementNode->mChildren.notNull()) - { - for(LLXMLNodePtr element = mElementNode->mChildren->tail; - element.notNull(); - element = element->mPrev) - { - std::string name; - if(element->getAttributeString("name", name) && name == element_name) - { - complex_type_node = element->mChildren->head; - break; - } - } - } - //create complex_type node - // - // - // - // - // - if(complex_type_node.isNull()) - { - complex_type_node = mElementNode->createChild("xs:element", false); - - complex_type_node->createChild("minOccurs", true)->setIntValue(min_count); - complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count); - complex_type_node->createChild("name", true)->setStringValue(element_name); - complex_type_node = complex_type_node->createChild("xs:complexType", false); - } - - addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values); - } -} - -void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector* possible_values) -{ - if (!attribute_name.empty()) - { - LLXMLNodePtr new_enum_type_node; - if (possible_values != NULL) - { - // custom attribute type, for example - // - // - // - // - // - // - new_enum_type_node = new LLXMLNode("xs:simpleType", false); - - LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false); - restriction_node->createChild("base", true)->setStringValue("xs:string"); - - for (std::vector::const_iterator it = possible_values->begin(); - it != possible_values->end(); - ++it) - { - LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false); - enum_node->createChild("value", true)->setStringValue(*it); - } - } - - string_set_t& attributes_written = mAttributesWritten[type_declaration_node]; - - string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name); - - // attribute not yet declared - if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it)) - { - attributes_written.insert(found_it, attribute_name); - - LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false); - - // attribute name - attribute_node->createChild("name", true)->setStringValue(attribute_name); - - if (new_enum_type_node.notNull()) - { - attribute_node->addChild(new_enum_type_node); - } - else - { - // simple attribute type - attribute_node->createChild("type", true)->setStringValue(type); - } - - // required or optional - attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional"); - } - // attribute exists...handle collision of same name attributes with potentially different types - else - { - LLXMLNodePtr attribute_declaration; - if (type_declaration_node.notNull()) - { - for(LLXMLNodePtr node = type_declaration_node->mChildren->tail; - node.notNull(); - node = node->mPrev) - { - std::string name; - if (node->getAttributeString("name", name) && name == attribute_name) - { - attribute_declaration = node; - break; - } - } - } - - bool new_type_is_enum = new_enum_type_node.notNull(); - bool existing_type_is_enum = !attribute_declaration->hasAttribute("type"); - - // either type is enum, revert to string in collision - // don't bother to check for enum equivalence - if (new_type_is_enum || existing_type_is_enum) - { - if (attribute_declaration->hasAttribute("type")) - { - attribute_declaration->setAttributeString("type", "xs:string"); - } - else - { - attribute_declaration->createChild("type", true)->setStringValue("xs:string"); - } - attribute_declaration->deleteChildren("xs:simpleType"); - } - else - { - // check for collision of different standard types - std::string existing_type; - attribute_declaration->getAttributeString("type", existing_type); - // if current type is not the same as the new type, revert to strnig - if (existing_type != type) - { - // ...than use most general type, string - attribute_declaration->setAttributeString("type", "string"); - } - } - } - } -} - -// -// LLXUIXSDWriter -// -void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block) -{ - std::string file_name(path); - file_name += type_name + ".xsd"; - LLXMLNodePtr root_nodep = new LLXMLNode(); - - LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui"); - - // add includes for all possible children - const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name); - const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type); - - // add choices for valid children - if (widget_registryp) - { - // add include declarations for all valid children - for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems(); - it != widget_registryp->currentRegistrar().endItems(); - ++it) - { - std::string widget_name = it->first; - if (widget_name == type_name) - { - continue; - } - LLXMLNodePtr nodep = new LLXMLNode("xs:include", false); - nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd"); - - // add to front of schema - mSchemaNode->addChild(nodep); - } - - for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems(); - it != widget_registryp->currentRegistrar().endItems(); - ++it) - { - std::string widget_name = it->first; - // - LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false); - widget_node->createChild("name", true)->setStringValue(widget_name); - widget_node->createChild("type", true)->setStringValue(widget_name); - } - } - - LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w"); - LLXMLNode::writeHeaderToFile(xsd_file); - root_nodep->writeToFile(xsd_file); - fclose(xsd_file); -} - -static LLInitParam::Parser::parser_read_func_map_t sXUIReadFuncs; -static LLInitParam::Parser::parser_write_func_map_t sXUIWriteFuncs; -static LLInitParam::Parser::parser_inspect_func_map_t sXUIInspectFuncs; - -// -// LLXUIParser -// -LLXUIParser::LLXUIParser() -: Parser(sXUIReadFuncs, sXUIWriteFuncs, sXUIInspectFuncs), - mCurReadDepth(0) -{ - if (sXUIReadFuncs.empty()) - { - registerParserFuncs(readFlag, writeFlag); - registerParserFuncs(readBoolValue, writeBoolValue); - registerParserFuncs(readStringValue, writeStringValue); - registerParserFuncs(readU8Value, writeU8Value); - registerParserFuncs(readS8Value, writeS8Value); - registerParserFuncs(readU16Value, writeU16Value); - registerParserFuncs(readS16Value, writeS16Value); - registerParserFuncs(readU32Value, writeU32Value); - registerParserFuncs(readS32Value, writeS32Value); - registerParserFuncs(readF32Value, writeF32Value); - registerParserFuncs(readF64Value, writeF64Value); - registerParserFuncs(readVector3Value, writeVector3Value); - registerParserFuncs(readColor4Value, writeColor4Value); - registerParserFuncs(readUIColorValue, writeUIColorValue); - registerParserFuncs(readUUIDValue, writeUUIDValue); - registerParserFuncs(readSDValue, writeSDValue); - } -} - -static LLTrace::BlockTimerStatHandle FTM_PARSE_XUI("XUI Parsing"); -const LLXMLNodePtr DUMMY_NODE = new LLXMLNode(); - -void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, const std::string& filename, bool silent) -{ - LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI); - mNameStack.clear(); - mRootNodeName = node->getName()->mString; - mCurFileName = filename; - mCurReadDepth = 0; - setParseSilently(silent); - - if (node.isNull()) - { - parserWarning("Invalid node"); - } - else - { - readXUIImpl(node, block); - } -} - -bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block) -{ - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("."); - - bool values_parsed = false; - bool silent = mCurReadDepth > 0; - - if (nodep->getFirstChild().isNull() - && nodep->mAttributes.empty() - && nodep->getSanitizedValue().empty()) - { - // empty node, just parse as flag - mCurReadNode = DUMMY_NODE; - return block.submitValue(mNameStack, *this, silent); - } - - // submit attributes for current node - values_parsed |= readAttributes(nodep, block); - - // treat text contents of xml node as "value" parameter - std::string text_contents = nodep->getSanitizedValue(); - if (!text_contents.empty()) - { - mCurReadNode = nodep; - mNameStack.push_back(std::make_pair(std::string("value"), true)); - // child nodes are not necessarily valid parameters (could be a child widget) - // so don't complain once we've recursed - if (!block.submitValue(mNameStack, *this, true)) - { - mNameStack.pop_back(); - block.submitValue(mNameStack, *this, silent); - } - else - { - mNameStack.pop_back(); - } - } - - // then traverse children - // child node must start with last name of parent node (our "scope") - // for example: "" - // which equates to the following nesting: - // button - // param - // nested_param1 - // nested_param2 - // nested_param3 - mCurReadDepth++; - for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();) - { - std::string child_name(childp->getName()->mString); - S32 num_tokens_pushed = 0; - - // for non "dotted" child nodes check to see if child node maps to another widget type - // and if not, treat as a child element of the current node - // e.g. will interpret as "button.rect" - // since there is no widget named "rect" - if (child_name.find(".") == std::string::npos) - { - mNameStack.push_back(std::make_pair(child_name, true)); - num_tokens_pushed++; - } - else - { - // parse out "dotted" name into individual tokens - tokenizer name_tokens(child_name, sep); - - tokenizer::iterator name_token_it = name_tokens.begin(); - if(name_token_it == name_tokens.end()) - { - childp = childp->getNextSibling(); - continue; - } - - // check for proper nesting - if (mNameStack.empty()) - { - if (*name_token_it != mRootNodeName) - { - childp = childp->getNextSibling(); - continue; - } - } - else if(mNameStack.back().first != *name_token_it) - { - childp = childp->getNextSibling(); - continue; - } - - // now ignore first token - ++name_token_it; - - // copy remaining tokens on to our running token list - for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push) - { - mNameStack.push_back(std::make_pair(*token_to_push, true)); - num_tokens_pushed++; - } - } - - // recurse and visit children XML nodes - if(readXUIImpl(childp, block)) - { - // child node successfully parsed, remove from DOM - - values_parsed = true; - LLXMLNodePtr node_to_remove = childp; - childp = childp->getNextSibling(); - - nodep->deleteChild(node_to_remove); - } - else - { - childp = childp->getNextSibling(); - } - - while(num_tokens_pushed-- > 0) - { - mNameStack.pop_back(); - } - } - mCurReadDepth--; - return values_parsed; -} - -bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block) -{ - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("."); - - bool any_parsed = false; - bool silent = mCurReadDepth > 0; - - for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); - attribute_it != nodep->mAttributes.end(); - ++attribute_it) - { - S32 num_tokens_pushed = 0; - std::string attribute_name(attribute_it->first->mString); - mCurReadNode = attribute_it->second; - - tokenizer name_tokens(attribute_name, sep); - // copy remaining tokens on to our running token list - for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push) - { - mNameStack.push_back(std::make_pair(*token_to_push, true)); - num_tokens_pushed++; - } - - // child nodes are not necessarily valid attributes, so don't complain once we've recursed - any_parsed |= block.submitValue(mNameStack, *this, silent); - - while(num_tokens_pushed-- > 0) - { - mNameStack.pop_back(); - } - } - - return any_parsed; -} - -void LLXUIParser::writeXUIImpl(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::predicate_rule_t rules, const LLInitParam::BaseBlock* diff_block) -{ - mWriteRootNode = node; - name_stack_t name_stack = Parser::name_stack_t(); - block.serializeBlock(*this, name_stack, rules, diff_block); - mOutNodes.clear(); -} - -// go from a stack of names to a specific XML node -LLXMLNodePtr LLXUIParser::getNode(name_stack_t& stack) -{ - LLXMLNodePtr out_node = mWriteRootNode; - - name_stack_t::iterator next_it = stack.begin(); - for (name_stack_t::iterator it = stack.begin(); - it != stack.end(); - it = next_it) - { - ++next_it; - bool force_new_node = false; - - if (it->first.empty()) - { - it->second = false; - continue; - } - - if (next_it != stack.end() && next_it->first.empty() && next_it->second) - { - force_new_node = true; - } - - - out_nodes_t::iterator found_it = mOutNodes.find(it->first); - - // node with this name not yet written - if (found_it == mOutNodes.end() || it->second || force_new_node) - { - // make an attribute if we are the last element on the name stack - bool is_attribute = next_it == stack.end(); - LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute); - out_node->addChild(new_node); - mOutNodes[it->first] = new_node; - out_node = new_node; - it->second = false; - } - else - { - out_node = found_it->second; - } - } - - return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node); -} - -bool LLXUIParser::readFlag(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - return self.mCurReadNode == DUMMY_NODE; -} - -bool LLXUIParser::writeFlag(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - // just create node - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - return node.notNull(); -} - -bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr) -{ - bool value; - LLXUIParser& self = static_cast(parser); - bool success = self.mCurReadNode->getBoolValue(1, &value); - *((bool*)val_ptr) = value; - return success; -} - -bool LLXUIParser::writeBoolValue(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setBoolValue(*((bool*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readStringValue(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - *((std::string*)val_ptr) = self.mCurReadNode->getSanitizedValue(); - return true; -} - -bool LLXUIParser::writeStringValue(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - const std::string* string_val = reinterpret_cast(val_ptr); - if (string_val->find('\n') != std::string::npos - || string_val->size() > MAX_STRING_ATTRIBUTE_SIZE) - { - // don't write strings with newlines into attributes - std::string attribute_name = node->getName()->mString; - LLXMLNodePtr parent_node = node->mParent; - parent_node->deleteChild(node); - // write results in text contents of node - if (attribute_name == "value") - { - // "value" is implicit, just write to parent - node = parent_node; - } - else - { - // create a child that is not an attribute, but with same name - node = parent_node->createChild(attribute_name.c_str(), false); - } - } - node->setStringValue(*string_val); - return true; - } - return false; -} - -bool LLXUIParser::readU8Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - return self.mCurReadNode->getByteValue(1, (U8*)val_ptr); -} - -bool LLXUIParser::writeU8Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setUnsignedValue(*((U8*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readS8Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - S32 value; - if(self.mCurReadNode->getIntValue(1, &value)) - { - *((S8*)val_ptr) = value; - return true; - } - return false; -} - -bool LLXUIParser::writeS8Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setIntValue(*((S8*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readU16Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - U32 value; - if(self.mCurReadNode->getUnsignedValue(1, &value)) - { - *((U16*)val_ptr) = value; - return true; - } - return false; -} - -bool LLXUIParser::writeU16Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setUnsignedValue(*((U16*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readS16Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - S32 value; - if(self.mCurReadNode->getIntValue(1, &value)) - { - *((S16*)val_ptr) = value; - return true; - } - return false; -} - -bool LLXUIParser::writeS16Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setIntValue(*((S16*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readU32Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - return self.mCurReadNode->getUnsignedValue(1, (U32*)val_ptr); -} - -bool LLXUIParser::writeU32Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setUnsignedValue(*((U32*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readS32Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - return self.mCurReadNode->getIntValue(1, (S32*)val_ptr); -} - -bool LLXUIParser::writeS32Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setIntValue(*((S32*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readF32Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - return self.mCurReadNode->getFloatValue(1, (F32*)val_ptr); -} - -bool LLXUIParser::writeF32Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setFloatValue(*((F32*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readF64Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - return self.mCurReadNode->getDoubleValue(1, (F64*)val_ptr); -} - -bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setDoubleValue(*((F64*)val_ptr)); - return true; - } - return false; -} - -bool LLXUIParser::readVector3Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - LLVector3* vecp = (LLVector3*)val_ptr; - if(self.mCurReadNode->getFloatValue(3, vecp->mV) >= 3) - { - return true; - } - - return false; -} - -bool LLXUIParser::writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - LLVector3 vector = *((LLVector3*)val_ptr); - node->setFloatValue(3, vector.mV); - return true; - } - return false; -} - -bool LLXUIParser::readColor4Value(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - LLColor4* colorp = (LLColor4*)val_ptr; - if(self.mCurReadNode->getFloatValue(4, colorp->mV) >= 3) - { - return true; - } - - return false; -} - -bool LLXUIParser::writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - LLColor4 color = *((LLColor4*)val_ptr); - node->setFloatValue(4, color.mV); - return true; - } - return false; -} - -bool LLXUIParser::readUIColorValue(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - LLUIColor* param = (LLUIColor*)val_ptr; - LLColor4 color; - bool success = self.mCurReadNode->getFloatValue(4, color.mV) >= 3; - if (success) - { - param->set(color); - return true; - } - return false; -} - -bool LLXUIParser::writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - LLUIColor color = *((LLUIColor*)val_ptr); - //RN: don't write out the color that is represented by a function - // rely on param block exporting to get the reference to the color settings - if (color.isReference()) return false; - node->setFloatValue(4, color.get().mV); - return true; - } - return false; -} - -bool LLXUIParser::readUUIDValue(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - LLUUID temp_id; - // LLUUID::set is destructive, so use temporary value - if (temp_id.set(self.mCurReadNode->getSanitizedValue())) - { - *(LLUUID*)(val_ptr) = temp_id; - return true; - } - return false; -} - -bool LLXUIParser::writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - node->setStringValue(((LLUUID*)val_ptr)->asString()); - return true; - } - return false; -} - -bool LLXUIParser::readSDValue(Parser& parser, void* val_ptr) -{ - LLXUIParser& self = static_cast(parser); - *((LLSD*)val_ptr) = LLSD(self.mCurReadNode->getSanitizedValue()); - return true; -} - -bool LLXUIParser::writeSDValue(Parser& parser, const void* val_ptr, name_stack_t& stack) -{ - LLXUIParser& self = static_cast(parser); - - LLXMLNodePtr node = self.getNode(stack); - if (node.notNull()) - { - std::string string_val = ((LLSD*)val_ptr)->asString(); - if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE) - { - // don't write strings with newlines into attributes - std::string attribute_name = node->getName()->mString; - LLXMLNodePtr parent_node = node->mParent; - parent_node->deleteChild(node); - // write results in text contents of node - if (attribute_name == "value") - { - // "value" is implicit, just write to parent - node = parent_node; - } - else - { - node = parent_node->createChild(attribute_name.c_str(), false); - } - } - - node->setStringValue(string_val); - return true; - } - return false; -} - -/*virtual*/ std::string LLXUIParser::getCurrentElementName() -{ - std::string full_name; - for (name_stack_t::iterator it = mNameStack.begin(); - it != mNameStack.end(); - ++it) - { - full_name += it->first + "."; // build up dotted names: "button.param.nestedparam." - } - - return full_name; -} - -void LLXUIParser::parserWarning(const std::string& message) -{ - std::string warning_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber()); - Parser::parserWarning(warning_msg); -} - -void LLXUIParser::parserError(const std::string& message) -{ - std::string error_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber()); - Parser::parserError(error_msg); -} - - -// -// LLSimpleXUIParser -// - -struct ScopedFile -{ - ScopedFile( const std::string& filename, const char* accessmode ) - { - mFile = LLFile::fopen(filename, accessmode); - } - - ~ScopedFile() - { - fclose(mFile); - mFile = NULL; - } - - S32 getRemainingBytes() - { - if (!isOpen()) return 0; - - S32 cur_pos = ftell(mFile); - fseek(mFile, 0L, SEEK_END); - S32 file_size = ftell(mFile); - fseek(mFile, cur_pos, SEEK_SET); - return file_size - cur_pos; - } - - bool isOpen() { return mFile != NULL; } - - LLFILE* mFile; -}; -LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t element_cb) -: Parser(sSimpleXUIReadFuncs, sSimpleXUIWriteFuncs, sSimpleXUIInspectFuncs), - mCurReadDepth(0), - mElementCB(element_cb) -{ - if (sSimpleXUIReadFuncs.empty()) - { - registerParserFuncs(readFlag); - registerParserFuncs(readBoolValue); - registerParserFuncs(readStringValue); - registerParserFuncs(readU8Value); - registerParserFuncs(readS8Value); - registerParserFuncs(readU16Value); - registerParserFuncs(readS16Value); - registerParserFuncs(readU32Value); - registerParserFuncs(readS32Value); - registerParserFuncs(readF32Value); - registerParserFuncs(readF64Value); - registerParserFuncs(readColor4Value); - registerParserFuncs(readUIColorValue); - registerParserFuncs(readUUIDValue); - registerParserFuncs(readSDValue); - } -} - -LLSimpleXUIParser::~LLSimpleXUIParser() -{ -} - - -bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBlock& block, bool silent) -{ - LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI); - - mParser = XML_ParserCreate(NULL); - XML_SetUserData(mParser, this); - XML_SetElementHandler( mParser, startElementHandler, endElementHandler); - XML_SetCharacterDataHandler( mParser, characterDataHandler); - - mOutputStack.push_back(std::make_pair(&block, 0)); - mNameStack.clear(); - mCurFileName = filename; - mCurReadDepth = 0; - setParseSilently(silent); - - ScopedFile file(filename, "rb"); - if( !file.isOpen() ) - { - LL_WARNS("ReadXUI") << "Unable to open file " << filename << LL_ENDL; - XML_ParserFree( mParser ); - return false; - } - - S32 bytes_read = 0; - - S32 buffer_size = file.getRemainingBytes(); - void* buffer = XML_GetBuffer(mParser, buffer_size); - if( !buffer ) - { - LL_WARNS("ReadXUI") << "Unable to allocate XML buffer while reading file " << filename << LL_ENDL; - XML_ParserFree( mParser ); - return false; - } - - bytes_read = (S32)fread(buffer, 1, buffer_size, file.mFile); - if( bytes_read <= 0 ) - { - LL_WARNS("ReadXUI") << "Error while reading file " << filename << LL_ENDL; - XML_ParserFree( mParser ); - return false; - } - - mEmptyLeafNode.push_back(false); - - if( !XML_ParseBuffer(mParser, bytes_read, true ) ) - { - LL_WARNS("ReadXUI") << "Error while parsing file " << filename << LL_ENDL; - XML_ParserFree( mParser ); - return false; - } - - mEmptyLeafNode.pop_back(); - - XML_ParserFree( mParser ); - return true; -} - -void LLSimpleXUIParser::startElementHandler(void *userData, const char *name, const char **atts) -{ - LLSimpleXUIParser* self = reinterpret_cast(userData); - self->startElement(name, atts); -} - -void LLSimpleXUIParser::endElementHandler(void *userData, const char *name) -{ - LLSimpleXUIParser* self = reinterpret_cast(userData); - self->endElement(name); -} - -void LLSimpleXUIParser::characterDataHandler(void *userData, const char *s, int len) -{ - LLSimpleXUIParser* self = reinterpret_cast(userData); - self->characterData(s, len); -} - -void LLSimpleXUIParser::characterData(const char *s, int len) -{ - mTextContents += std::string(s, len); -} - -void LLSimpleXUIParser::startElement(const char *name, const char **atts) -{ - processText(); - - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("."); - - if (mElementCB) - { - LLInitParam::BaseBlock* blockp = mElementCB(*this, name); - if (blockp) - { - mOutputStack.push_back(std::make_pair(blockp, 0)); - } - } - - mOutputStack.back().second++; - S32 num_tokens_pushed = 0; - std::string child_name(name); - - if (mOutputStack.back().second == 1) - { // root node for this block - mScope.push_back(child_name); - } - else - { // compound attribute - if (child_name.find(".") == std::string::npos) - { - mNameStack.push_back(std::make_pair(child_name, true)); - num_tokens_pushed++; - mScope.push_back(child_name); - } - else - { - // parse out "dotted" name into individual tokens - tokenizer name_tokens(child_name, sep); - - tokenizer::iterator name_token_it = name_tokens.begin(); - if(name_token_it == name_tokens.end()) - { - return; - } - - // check for proper nesting - if(!mScope.empty() && *name_token_it != mScope.back()) - { - return; - } - - // now ignore first token - ++name_token_it; - - // copy remaining tokens on to our running token list - for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push) - { - mNameStack.push_back(std::make_pair(*token_to_push, true)); - num_tokens_pushed++; - } - mScope.push_back(mNameStack.back().first); - } - } - - // parent node is not empty - mEmptyLeafNode.back() = false; - // we are empty if we have no attributes - mEmptyLeafNode.push_back(atts[0] == NULL); - - mTokenSizeStack.push_back(num_tokens_pushed); - readAttributes(atts); - -} - -void LLSimpleXUIParser::endElement(const char *name) -{ - bool has_text = processText(); - - // no text, attributes, or children - if (!has_text && mEmptyLeafNode.back()) - { - // submit this as a valueless name (even though there might be text contents we haven't seen yet) - mCurAttributeValueBegin = NO_VALUE_MARKER; - mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently); - } - - if (--mOutputStack.back().second == 0) - { - if (mOutputStack.empty()) - { - LL_ERRS("ReadXUI") << "Parameter block output stack popped while empty." << LL_ENDL; - } - mOutputStack.pop_back(); - } - - S32 num_tokens_to_pop = mTokenSizeStack.back(); - mTokenSizeStack.pop_back(); - while(num_tokens_to_pop-- > 0) - { - mNameStack.pop_back(); - } - mScope.pop_back(); - mEmptyLeafNode.pop_back(); -} - -bool LLSimpleXUIParser::readAttributes(const char **atts) -{ - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("."); - - bool any_parsed = false; - for(S32 i = 0; atts[i] && atts[i+1]; i += 2 ) - { - std::string attribute_name(atts[i]); - mCurAttributeValueBegin = atts[i+1]; - - S32 num_tokens_pushed = 0; - tokenizer name_tokens(attribute_name, sep); - // copy remaining tokens on to our running token list - for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push) - { - mNameStack.push_back(std::make_pair(*token_to_push, true)); - num_tokens_pushed++; - } - - // child nodes are not necessarily valid attributes, so don't complain once we've recursed - any_parsed |= mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently); - - while(num_tokens_pushed-- > 0) - { - mNameStack.pop_back(); - } - } - return any_parsed; -} - -bool LLSimpleXUIParser::processText() -{ - if (!mTextContents.empty()) - { - LLStringUtil::trim(mTextContents); - if (!mTextContents.empty()) - { - mNameStack.push_back(std::make_pair(std::string("value"), true)); - mCurAttributeValueBegin = mTextContents.c_str(); - mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently); - mNameStack.pop_back(); - } - mTextContents.clear(); - return true; - } - return false; -} - -/*virtual*/ std::string LLSimpleXUIParser::getCurrentElementName() -{ - std::string full_name; - for (name_stack_t::iterator it = mNameStack.begin(); - it != mNameStack.end(); - ++it) - { - full_name += it->first + "."; // build up dotted names: "button.param.nestedparam." - } - - return full_name; -} - -void LLSimpleXUIParser::parserWarning(const std::string& message) -{ - std::string warning_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str()); - Parser::parserWarning(warning_msg); -} - -void LLSimpleXUIParser::parserError(const std::string& message) -{ - std::string error_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str()); - Parser::parserError(error_msg); -} - -bool LLSimpleXUIParser::readFlag(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return self.mCurAttributeValueBegin == NO_VALUE_MARKER; -} - -bool LLSimpleXUIParser::readBoolValue(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - if (!strcmp(self.mCurAttributeValueBegin, "true")) - { - *((bool*)val_ptr) = true; - return true; - } - else if (!strcmp(self.mCurAttributeValueBegin, "false")) - { - *((bool*)val_ptr) = false; - return true; - } - - return false; -} - -bool LLSimpleXUIParser::readStringValue(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - *((std::string*)val_ptr) = self.mCurAttributeValueBegin; - return true; -} - -bool LLSimpleXUIParser::readU8Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U8*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readS8Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S8*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readU16Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U16*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readS16Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S16*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readU32Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U32*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readS32Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S32*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readF32Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F32*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readF64Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F64*)val_ptr)]).full; -} - -bool LLSimpleXUIParser::readColor4Value(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - LLColor4 value; - - if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full) - { - *(LLColor4*)(val_ptr) = value; - return true; - } - return false; -} - -bool LLSimpleXUIParser::readUIColorValue(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - LLColor4 value; - LLUIColor* colorp = (LLUIColor*)val_ptr; - - if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full) - { - colorp->set(value); - return true; - } - return false; -} - -bool LLSimpleXUIParser::readUUIDValue(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - LLUUID temp_id; - // LLUUID::set is destructive, so use temporary value - if (temp_id.set(std::string(self.mCurAttributeValueBegin))) - { - *(LLUUID*)(val_ptr) = temp_id; - return true; - } - return false; -} - -bool LLSimpleXUIParser::readSDValue(Parser& parser, void* val_ptr) -{ - LLSimpleXUIParser& self = static_cast(parser); - *((LLSD*)val_ptr) = LLSD(self.mCurAttributeValueBegin); - return true; -} +/** + * @file llxuiparser.cpp + * @brief Utility functions for handling XUI structures in XML + * + * $LicenseInfo:firstyear=2003&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 "llxuiparser.h" + +#include "llxmlnode.h" +#include "llfasttimer.h" +#ifdef LL_USESYSTEMLIBS +#include +#else +#include "expat/expat.h" +#endif + +#include +#include +#include +//#include +#include + +#include "lluicolor.h" +#include "v3math.h" +using namespace BOOST_SPIRIT_CLASSIC_NS; + +const S32 MAX_STRING_ATTRIBUTE_SIZE = 40; + +static LLInitParam::Parser::parser_read_func_map_t sXSDReadFuncs; +static LLInitParam::Parser::parser_write_func_map_t sXSDWriteFuncs; +static LLInitParam::Parser::parser_inspect_func_map_t sXSDInspectFuncs; + +static LLInitParam::Parser::parser_read_func_map_t sSimpleXUIReadFuncs; +static LLInitParam::Parser::parser_write_func_map_t sSimpleXUIWriteFuncs; +static LLInitParam::Parser::parser_inspect_func_map_t sSimpleXUIInspectFuncs; + +const char* NO_VALUE_MARKER = "no_value"; + +struct MaxOccursValues : public LLInitParam::TypeValuesHelper +{ + static void declareValues() + { + declare("unbounded", U32_MAX); + } +}; + +struct Occurs : public LLInitParam::Block +{ + Optional minOccurs; + Optional maxOccurs; + + Occurs() + : minOccurs("minOccurs", 0), + maxOccurs("maxOccurs", U32_MAX) + + {} +}; + +typedef enum +{ + USE_REQUIRED, + USE_OPTIONAL +} EUse; + +namespace LLInitParam +{ + template<> + struct TypeValues : public TypeValuesHelper + { + static void declareValues() + { + declare("required", USE_REQUIRED); + declare("optional", USE_OPTIONAL); + } + }; +} + +struct Element; +struct Group; +struct Sequence; + +struct All : public LLInitParam::Block +{ + Multiple< Lazy > elements; + + All() + : elements("element") + { + maxOccurs = 1; + } +}; + +struct Attribute : public LLInitParam::Block +{ + Mandatory name, + type; + Mandatory use; + + Attribute() + : name("name"), + type("type"), + use("use") + {} +}; + +struct Any : public LLInitParam::Block +{ + Optional _namespace; + + Any() + : _namespace("namespace") + {} +}; + +struct Choice : public LLInitParam::ChoiceBlock +{ + Alternative< Lazy > element; + Alternative< Lazy > group; + Alternative< Lazy > choice; + Alternative< Lazy > sequence; + Alternative< Lazy > any; + + Choice() + : element("element"), + group("group"), + choice("choice"), + sequence("sequence"), + any("any") + {} + +}; + +struct Sequence : public LLInitParam::ChoiceBlock +{ + Alternative< Lazy > element; + Alternative< Lazy > group; + Alternative< Lazy > choice; + Alternative< Lazy > sequence; + Alternative< Lazy > any; +}; + +struct GroupContents : public LLInitParam::ChoiceBlock +{ + Alternative all; + Alternative choice; + Alternative sequence; + + GroupContents() + : all("all"), + choice("choice"), + sequence("sequence") + {} +}; + +struct Group : public LLInitParam::Block +{ + Optional name, + ref; + + Group() + : name("name"), + ref("ref") + {} +}; + +struct Restriction : public LLInitParam::Block +{ +}; + +struct Extension : public LLInitParam::Block +{ +}; + +struct SimpleContent : public LLInitParam::ChoiceBlock +{ + Alternative restriction; + Alternative extension; + + SimpleContent() + : restriction("restriction"), + extension("extension") + {} +}; + +struct SimpleType : public LLInitParam::Block +{ + // TODO +}; + +struct ComplexContent : public LLInitParam::Block +{ + Optional mixed; + + ComplexContent() + : mixed("mixed", true) + {} +}; + +struct ComplexTypeContents : public LLInitParam::ChoiceBlock +{ + Alternative simple_content; + Alternative complex_content; + Alternative group; + Alternative all; + Alternative choice; + Alternative sequence; + + ComplexTypeContents() + : simple_content("simpleContent"), + complex_content("complexContent"), + group("group"), + all("all"), + choice("choice"), + sequence("sequence") + {} +}; + +struct ComplexType : public LLInitParam::Block +{ + Optional name; + Optional mixed; + + Multiple attribute; + Multiple< Lazy > elements; + + ComplexType() + : name("name"), + attribute("xs:attribute"), + elements("xs:element"), + mixed("mixed") + { + } +}; + +struct ElementContents : public LLInitParam::ChoiceBlock +{ + Alternative simpleType; + Alternative complexType; + + ElementContents() + : simpleType("simpleType"), + complexType("complexType") + {} +}; + +struct Element : public LLInitParam::Block +{ + Optional name, + ref, + type; + + Element() + : name("xs:name"), + ref("xs:ref"), + type("xs:type") + {} +}; + +struct Schema : public LLInitParam::Block +{ +private: + Mandatory targetNamespace, + xmlns, + xs; + +public: + Optional attributeFormDefault, + elementFormDefault; + + Mandatory root_element; + + void setNameSpace(const std::string& ns) {targetNamespace = ns; xmlns = ns;} + + Schema(const std::string& ns = LLStringUtil::null) + : attributeFormDefault("attributeFormDefault"), + elementFormDefault("elementFormDefault"), + xs("xmlns:xs"), + targetNamespace("targetNamespace"), + xmlns("xmlns"), + root_element("xs:element") + { + attributeFormDefault = "unqualified"; + elementFormDefault = "qualified"; + xs = "http://www.w3.org/2001/XMLSchema"; + if (!ns.empty()) + { + setNameSpace(ns); + }; + } +}; + +// +// LLXSDWriter +// +LLXSDWriter::LLXSDWriter() +: Parser(sXSDReadFuncs, sXSDWriteFuncs, sXSDInspectFuncs) +{ + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); + registerInspectFunc(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4)); +} + +LLXSDWriter::~LLXSDWriter() {} + +void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace) +{ + Schema schema(xml_namespace); + + schema.root_element.name = type_name; + Choice& choice = schema.root_element.complexType.choice; + + choice.minOccurs = 0; + choice.maxOccurs = "unbounded"; + + mSchemaNode = node; + //node->setName("xs:schema"); + //node->createChild("attributeFormDefault", true)->setStringValue("unqualified"); + //node->createChild("elementFormDefault", true)->setStringValue("qualified"); + //node->createChild("targetNamespace", true)->setStringValue(xml_namespace); + //node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema"); + //node->createChild("xmlns", true)->setStringValue(xml_namespace); + + //node = node->createChild("xs:complexType", false); + //node->createChild("name", true)->setStringValue(type_name); + //node->createChild("mixed", true)->setStringValue("true"); + + //mAttributeNode = node; + //mElementNode = node->createChild("xs:choice", false); + //mElementNode->createChild("minOccurs", true)->setStringValue("0"); + //mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded"); + block.inspectBlock(*this); + + // duplicate element choices + LLXMLNodeList children; + mElementNode->getChildren("xs:element", children, false); + for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it) + { + LLXMLNodePtr child_copy = child_it->second->deepCopy(); + std::string child_name; + child_copy->getAttributeString("name", child_name); + child_copy->setAttributeString("name", type_name + "." + child_name); + mElementNode->addChild(child_copy); + } + + LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false); + element_declaration_node->createChild("name", true)->setStringValue(type_name); + element_declaration_node->createChild("type", true)->setStringValue(type_name); +} + +void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector* possible_values) +{ + name_stack_t non_empty_names; + std::string attribute_name; + for (name_stack_t::const_iterator it = stack.begin(); + it != stack.end(); + ++it) + { + const std::string& name = it->first; + if (!name.empty()) + { + non_empty_names.push_back(*it); + } + } + + for (name_stack_t::const_iterator it = non_empty_names.begin(); + it != non_empty_names.end(); + ++it) + { + if (!attribute_name.empty()) + { + attribute_name += "."; + } + attribute_name += it->first; + } + + // only flag non-nested attributes as mandatory, nested attributes have variant syntax + // that can't be properly constrained in XSD + // e.g. vs + bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1; + + // don't bother supporting "Multiple" params as xml attributes + if (max_count <= 1) + { + // add compound attribute to root node + addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values); + } + + // now generated nested elements for compound attributes + if (non_empty_names.size() > 1 && !attribute_mandatory) + { + std::string element_name; + + // traverse all but last element, leaving that as an attribute name + name_stack_t::const_iterator end_it = non_empty_names.end(); + end_it--; + + for (name_stack_t::const_iterator it = non_empty_names.begin(); + it != end_it; + ++it) + { + if (it != non_empty_names.begin()) + { + element_name += "."; + } + element_name += it->first; + } + + std::string short_attribute_name = non_empty_names.back().first; + + LLXMLNodePtr complex_type_node; + + // find existing element node here, starting at tail of child list + if (mElementNode->mChildren.notNull()) + { + for(LLXMLNodePtr element = mElementNode->mChildren->tail; + element.notNull(); + element = element->mPrev) + { + std::string name; + if(element->getAttributeString("name", name) && name == element_name) + { + complex_type_node = element->mChildren->head; + break; + } + } + } + //create complex_type node + // + // + // + // + // + if(complex_type_node.isNull()) + { + complex_type_node = mElementNode->createChild("xs:element", false); + + complex_type_node->createChild("minOccurs", true)->setIntValue(min_count); + complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count); + complex_type_node->createChild("name", true)->setStringValue(element_name); + complex_type_node = complex_type_node->createChild("xs:complexType", false); + } + + addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values); + } +} + +void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector* possible_values) +{ + if (!attribute_name.empty()) + { + LLXMLNodePtr new_enum_type_node; + if (possible_values != NULL) + { + // custom attribute type, for example + // + // + // + // + // + // + new_enum_type_node = new LLXMLNode("xs:simpleType", false); + + LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false); + restriction_node->createChild("base", true)->setStringValue("xs:string"); + + for (std::vector::const_iterator it = possible_values->begin(); + it != possible_values->end(); + ++it) + { + LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false); + enum_node->createChild("value", true)->setStringValue(*it); + } + } + + string_set_t& attributes_written = mAttributesWritten[type_declaration_node]; + + string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name); + + // attribute not yet declared + if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it)) + { + attributes_written.insert(found_it, attribute_name); + + LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false); + + // attribute name + attribute_node->createChild("name", true)->setStringValue(attribute_name); + + if (new_enum_type_node.notNull()) + { + attribute_node->addChild(new_enum_type_node); + } + else + { + // simple attribute type + attribute_node->createChild("type", true)->setStringValue(type); + } + + // required or optional + attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional"); + } + // attribute exists...handle collision of same name attributes with potentially different types + else + { + LLXMLNodePtr attribute_declaration; + if (type_declaration_node.notNull()) + { + for(LLXMLNodePtr node = type_declaration_node->mChildren->tail; + node.notNull(); + node = node->mPrev) + { + std::string name; + if (node->getAttributeString("name", name) && name == attribute_name) + { + attribute_declaration = node; + break; + } + } + } + + bool new_type_is_enum = new_enum_type_node.notNull(); + bool existing_type_is_enum = !attribute_declaration->hasAttribute("type"); + + // either type is enum, revert to string in collision + // don't bother to check for enum equivalence + if (new_type_is_enum || existing_type_is_enum) + { + if (attribute_declaration->hasAttribute("type")) + { + attribute_declaration->setAttributeString("type", "xs:string"); + } + else + { + attribute_declaration->createChild("type", true)->setStringValue("xs:string"); + } + attribute_declaration->deleteChildren("xs:simpleType"); + } + else + { + // check for collision of different standard types + std::string existing_type; + attribute_declaration->getAttributeString("type", existing_type); + // if current type is not the same as the new type, revert to strnig + if (existing_type != type) + { + // ...than use most general type, string + attribute_declaration->setAttributeString("type", "string"); + } + } + } + } +} + +// +// LLXUIXSDWriter +// +void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block) +{ + std::string file_name(path); + file_name += type_name + ".xsd"; + LLXMLNodePtr root_nodep = new LLXMLNode(); + + LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui"); + + // add includes for all possible children + const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name); + const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type); + + // add choices for valid children + if (widget_registryp) + { + // add include declarations for all valid children + for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems(); + it != widget_registryp->currentRegistrar().endItems(); + ++it) + { + std::string widget_name = it->first; + if (widget_name == type_name) + { + continue; + } + LLXMLNodePtr nodep = new LLXMLNode("xs:include", false); + nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd"); + + // add to front of schema + mSchemaNode->addChild(nodep); + } + + for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems(); + it != widget_registryp->currentRegistrar().endItems(); + ++it) + { + std::string widget_name = it->first; + // + LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false); + widget_node->createChild("name", true)->setStringValue(widget_name); + widget_node->createChild("type", true)->setStringValue(widget_name); + } + } + + LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w"); + LLXMLNode::writeHeaderToFile(xsd_file); + root_nodep->writeToFile(xsd_file); + fclose(xsd_file); +} + +static LLInitParam::Parser::parser_read_func_map_t sXUIReadFuncs; +static LLInitParam::Parser::parser_write_func_map_t sXUIWriteFuncs; +static LLInitParam::Parser::parser_inspect_func_map_t sXUIInspectFuncs; + +// +// LLXUIParser +// +LLXUIParser::LLXUIParser() +: Parser(sXUIReadFuncs, sXUIWriteFuncs, sXUIInspectFuncs), + mCurReadDepth(0) +{ + if (sXUIReadFuncs.empty()) + { + registerParserFuncs(readFlag, writeFlag); + registerParserFuncs(readBoolValue, writeBoolValue); + registerParserFuncs(readStringValue, writeStringValue); + registerParserFuncs(readU8Value, writeU8Value); + registerParserFuncs(readS8Value, writeS8Value); + registerParserFuncs(readU16Value, writeU16Value); + registerParserFuncs(readS16Value, writeS16Value); + registerParserFuncs(readU32Value, writeU32Value); + registerParserFuncs(readS32Value, writeS32Value); + registerParserFuncs(readF32Value, writeF32Value); + registerParserFuncs(readF64Value, writeF64Value); + registerParserFuncs(readVector3Value, writeVector3Value); + registerParserFuncs(readColor4Value, writeColor4Value); + registerParserFuncs(readUIColorValue, writeUIColorValue); + registerParserFuncs(readUUIDValue, writeUUIDValue); + registerParserFuncs(readSDValue, writeSDValue); + } +} + +static LLTrace::BlockTimerStatHandle FTM_PARSE_XUI("XUI Parsing"); +const LLXMLNodePtr DUMMY_NODE = new LLXMLNode(); + +void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, const std::string& filename, bool silent) +{ + LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI); + mNameStack.clear(); + mRootNodeName = node->getName()->mString; + mCurFileName = filename; + mCurReadDepth = 0; + setParseSilently(silent); + + if (node.isNull()) + { + parserWarning("Invalid node"); + } + else + { + readXUIImpl(node, block); + } +} + +bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block) +{ + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("."); + + bool values_parsed = false; + bool silent = mCurReadDepth > 0; + + if (nodep->getFirstChild().isNull() + && nodep->mAttributes.empty() + && nodep->getSanitizedValue().empty()) + { + // empty node, just parse as flag + mCurReadNode = DUMMY_NODE; + return block.submitValue(mNameStack, *this, silent); + } + + // submit attributes for current node + values_parsed |= readAttributes(nodep, block); + + // treat text contents of xml node as "value" parameter + std::string text_contents = nodep->getSanitizedValue(); + if (!text_contents.empty()) + { + mCurReadNode = nodep; + mNameStack.push_back(std::make_pair(std::string("value"), true)); + // child nodes are not necessarily valid parameters (could be a child widget) + // so don't complain once we've recursed + if (!block.submitValue(mNameStack, *this, true)) + { + mNameStack.pop_back(); + block.submitValue(mNameStack, *this, silent); + } + else + { + mNameStack.pop_back(); + } + } + + // then traverse children + // child node must start with last name of parent node (our "scope") + // for example: "" + // which equates to the following nesting: + // button + // param + // nested_param1 + // nested_param2 + // nested_param3 + mCurReadDepth++; + for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();) + { + std::string child_name(childp->getName()->mString); + S32 num_tokens_pushed = 0; + + // for non "dotted" child nodes check to see if child node maps to another widget type + // and if not, treat as a child element of the current node + // e.g. will interpret as "button.rect" + // since there is no widget named "rect" + if (child_name.find(".") == std::string::npos) + { + mNameStack.push_back(std::make_pair(child_name, true)); + num_tokens_pushed++; + } + else + { + // parse out "dotted" name into individual tokens + tokenizer name_tokens(child_name, sep); + + tokenizer::iterator name_token_it = name_tokens.begin(); + if(name_token_it == name_tokens.end()) + { + childp = childp->getNextSibling(); + continue; + } + + // check for proper nesting + if (mNameStack.empty()) + { + if (*name_token_it != mRootNodeName) + { + childp = childp->getNextSibling(); + continue; + } + } + else if(mNameStack.back().first != *name_token_it) + { + childp = childp->getNextSibling(); + continue; + } + + // now ignore first token + ++name_token_it; + + // copy remaining tokens on to our running token list + for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push) + { + mNameStack.push_back(std::make_pair(*token_to_push, true)); + num_tokens_pushed++; + } + } + + // recurse and visit children XML nodes + if(readXUIImpl(childp, block)) + { + // child node successfully parsed, remove from DOM + + values_parsed = true; + LLXMLNodePtr node_to_remove = childp; + childp = childp->getNextSibling(); + + nodep->deleteChild(node_to_remove); + } + else + { + childp = childp->getNextSibling(); + } + + while(num_tokens_pushed-- > 0) + { + mNameStack.pop_back(); + } + } + mCurReadDepth--; + return values_parsed; +} + +bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block) +{ + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("."); + + bool any_parsed = false; + bool silent = mCurReadDepth > 0; + + for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); + attribute_it != nodep->mAttributes.end(); + ++attribute_it) + { + S32 num_tokens_pushed = 0; + std::string attribute_name(attribute_it->first->mString); + mCurReadNode = attribute_it->second; + + tokenizer name_tokens(attribute_name, sep); + // copy remaining tokens on to our running token list + for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push) + { + mNameStack.push_back(std::make_pair(*token_to_push, true)); + num_tokens_pushed++; + } + + // child nodes are not necessarily valid attributes, so don't complain once we've recursed + any_parsed |= block.submitValue(mNameStack, *this, silent); + + while(num_tokens_pushed-- > 0) + { + mNameStack.pop_back(); + } + } + + return any_parsed; +} + +void LLXUIParser::writeXUIImpl(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::predicate_rule_t rules, const LLInitParam::BaseBlock* diff_block) +{ + mWriteRootNode = node; + name_stack_t name_stack = Parser::name_stack_t(); + block.serializeBlock(*this, name_stack, rules, diff_block); + mOutNodes.clear(); +} + +// go from a stack of names to a specific XML node +LLXMLNodePtr LLXUIParser::getNode(name_stack_t& stack) +{ + LLXMLNodePtr out_node = mWriteRootNode; + + name_stack_t::iterator next_it = stack.begin(); + for (name_stack_t::iterator it = stack.begin(); + it != stack.end(); + it = next_it) + { + ++next_it; + bool force_new_node = false; + + if (it->first.empty()) + { + it->second = false; + continue; + } + + if (next_it != stack.end() && next_it->first.empty() && next_it->second) + { + force_new_node = true; + } + + + out_nodes_t::iterator found_it = mOutNodes.find(it->first); + + // node with this name not yet written + if (found_it == mOutNodes.end() || it->second || force_new_node) + { + // make an attribute if we are the last element on the name stack + bool is_attribute = next_it == stack.end(); + LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute); + out_node->addChild(new_node); + mOutNodes[it->first] = new_node; + out_node = new_node; + it->second = false; + } + else + { + out_node = found_it->second; + } + } + + return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node); +} + +bool LLXUIParser::readFlag(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + return self.mCurReadNode == DUMMY_NODE; +} + +bool LLXUIParser::writeFlag(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + // just create node + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + return node.notNull(); +} + +bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr) +{ + bool value; + LLXUIParser& self = static_cast(parser); + bool success = self.mCurReadNode->getBoolValue(1, &value); + *((bool*)val_ptr) = value; + return success; +} + +bool LLXUIParser::writeBoolValue(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setBoolValue(*((bool*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readStringValue(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + *((std::string*)val_ptr) = self.mCurReadNode->getSanitizedValue(); + return true; +} + +bool LLXUIParser::writeStringValue(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + const std::string* string_val = reinterpret_cast(val_ptr); + if (string_val->find('\n') != std::string::npos + || string_val->size() > MAX_STRING_ATTRIBUTE_SIZE) + { + // don't write strings with newlines into attributes + std::string attribute_name = node->getName()->mString; + LLXMLNodePtr parent_node = node->mParent; + parent_node->deleteChild(node); + // write results in text contents of node + if (attribute_name == "value") + { + // "value" is implicit, just write to parent + node = parent_node; + } + else + { + // create a child that is not an attribute, but with same name + node = parent_node->createChild(attribute_name.c_str(), false); + } + } + node->setStringValue(*string_val); + return true; + } + return false; +} + +bool LLXUIParser::readU8Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + return self.mCurReadNode->getByteValue(1, (U8*)val_ptr); +} + +bool LLXUIParser::writeU8Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setUnsignedValue(*((U8*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readS8Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + S32 value; + if(self.mCurReadNode->getIntValue(1, &value)) + { + *((S8*)val_ptr) = value; + return true; + } + return false; +} + +bool LLXUIParser::writeS8Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setIntValue(*((S8*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readU16Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + U32 value; + if(self.mCurReadNode->getUnsignedValue(1, &value)) + { + *((U16*)val_ptr) = value; + return true; + } + return false; +} + +bool LLXUIParser::writeU16Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setUnsignedValue(*((U16*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readS16Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + S32 value; + if(self.mCurReadNode->getIntValue(1, &value)) + { + *((S16*)val_ptr) = value; + return true; + } + return false; +} + +bool LLXUIParser::writeS16Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setIntValue(*((S16*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readU32Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + return self.mCurReadNode->getUnsignedValue(1, (U32*)val_ptr); +} + +bool LLXUIParser::writeU32Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setUnsignedValue(*((U32*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readS32Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + return self.mCurReadNode->getIntValue(1, (S32*)val_ptr); +} + +bool LLXUIParser::writeS32Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setIntValue(*((S32*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readF32Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + return self.mCurReadNode->getFloatValue(1, (F32*)val_ptr); +} + +bool LLXUIParser::writeF32Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setFloatValue(*((F32*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readF64Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + return self.mCurReadNode->getDoubleValue(1, (F64*)val_ptr); +} + +bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setDoubleValue(*((F64*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readVector3Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + LLVector3* vecp = (LLVector3*)val_ptr; + if(self.mCurReadNode->getFloatValue(3, vecp->mV) >= 3) + { + return true; + } + + return false; +} + +bool LLXUIParser::writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + LLVector3 vector = *((LLVector3*)val_ptr); + node->setFloatValue(3, vector.mV); + return true; + } + return false; +} + +bool LLXUIParser::readColor4Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + LLColor4* colorp = (LLColor4*)val_ptr; + if(self.mCurReadNode->getFloatValue(4, colorp->mV) >= 3) + { + return true; + } + + return false; +} + +bool LLXUIParser::writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + LLColor4 color = *((LLColor4*)val_ptr); + node->setFloatValue(4, color.mV); + return true; + } + return false; +} + +bool LLXUIParser::readUIColorValue(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + LLUIColor* param = (LLUIColor*)val_ptr; + LLColor4 color; + bool success = self.mCurReadNode->getFloatValue(4, color.mV) >= 3; + if (success) + { + param->set(color); + return true; + } + return false; +} + +bool LLXUIParser::writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + LLUIColor color = *((LLUIColor*)val_ptr); + //RN: don't write out the color that is represented by a function + // rely on param block exporting to get the reference to the color settings + if (color.isReference()) return false; + node->setFloatValue(4, color.get().mV); + return true; + } + return false; +} + +bool LLXUIParser::readUUIDValue(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + LLUUID temp_id; + // LLUUID::set is destructive, so use temporary value + if (temp_id.set(self.mCurReadNode->getSanitizedValue())) + { + *(LLUUID*)(val_ptr) = temp_id; + return true; + } + return false; +} + +bool LLXUIParser::writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + node->setStringValue(((LLUUID*)val_ptr)->asString()); + return true; + } + return false; +} + +bool LLXUIParser::readSDValue(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + *((LLSD*)val_ptr) = LLSD(self.mCurReadNode->getSanitizedValue()); + return true; +} + +bool LLXUIParser::writeSDValue(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + std::string string_val = ((LLSD*)val_ptr)->asString(); + if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE) + { + // don't write strings with newlines into attributes + std::string attribute_name = node->getName()->mString; + LLXMLNodePtr parent_node = node->mParent; + parent_node->deleteChild(node); + // write results in text contents of node + if (attribute_name == "value") + { + // "value" is implicit, just write to parent + node = parent_node; + } + else + { + node = parent_node->createChild(attribute_name.c_str(), false); + } + } + + node->setStringValue(string_val); + return true; + } + return false; +} + +/*virtual*/ std::string LLXUIParser::getCurrentElementName() +{ + std::string full_name; + for (name_stack_t::iterator it = mNameStack.begin(); + it != mNameStack.end(); + ++it) + { + full_name += it->first + "."; // build up dotted names: "button.param.nestedparam." + } + + return full_name; +} + +void LLXUIParser::parserWarning(const std::string& message) +{ + std::string warning_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber()); + Parser::parserWarning(warning_msg); +} + +void LLXUIParser::parserError(const std::string& message) +{ + std::string error_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber()); + Parser::parserError(error_msg); +} + + +// +// LLSimpleXUIParser +// + +struct ScopedFile +{ + ScopedFile( const std::string& filename, const char* accessmode ) + { + mFile = LLFile::fopen(filename, accessmode); + } + + ~ScopedFile() + { + fclose(mFile); + mFile = NULL; + } + + S32 getRemainingBytes() + { + if (!isOpen()) return 0; + + S32 cur_pos = ftell(mFile); + fseek(mFile, 0L, SEEK_END); + S32 file_size = ftell(mFile); + fseek(mFile, cur_pos, SEEK_SET); + return file_size - cur_pos; + } + + bool isOpen() { return mFile != NULL; } + + LLFILE* mFile; +}; +LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t element_cb) +: Parser(sSimpleXUIReadFuncs, sSimpleXUIWriteFuncs, sSimpleXUIInspectFuncs), + mCurReadDepth(0), + mElementCB(element_cb) +{ + if (sSimpleXUIReadFuncs.empty()) + { + registerParserFuncs(readFlag); + registerParserFuncs(readBoolValue); + registerParserFuncs(readStringValue); + registerParserFuncs(readU8Value); + registerParserFuncs(readS8Value); + registerParserFuncs(readU16Value); + registerParserFuncs(readS16Value); + registerParserFuncs(readU32Value); + registerParserFuncs(readS32Value); + registerParserFuncs(readF32Value); + registerParserFuncs(readF64Value); + registerParserFuncs(readColor4Value); + registerParserFuncs(readUIColorValue); + registerParserFuncs(readUUIDValue); + registerParserFuncs(readSDValue); + } +} + +LLSimpleXUIParser::~LLSimpleXUIParser() +{ +} + + +bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBlock& block, bool silent) +{ + LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI); + + mParser = XML_ParserCreate(NULL); + XML_SetUserData(mParser, this); + XML_SetElementHandler( mParser, startElementHandler, endElementHandler); + XML_SetCharacterDataHandler( mParser, characterDataHandler); + + mOutputStack.push_back(std::make_pair(&block, 0)); + mNameStack.clear(); + mCurFileName = filename; + mCurReadDepth = 0; + setParseSilently(silent); + + ScopedFile file(filename, "rb"); + if( !file.isOpen() ) + { + LL_WARNS("ReadXUI") << "Unable to open file " << filename << LL_ENDL; + XML_ParserFree( mParser ); + return false; + } + + S32 bytes_read = 0; + + S32 buffer_size = file.getRemainingBytes(); + void* buffer = XML_GetBuffer(mParser, buffer_size); + if( !buffer ) + { + LL_WARNS("ReadXUI") << "Unable to allocate XML buffer while reading file " << filename << LL_ENDL; + XML_ParserFree( mParser ); + return false; + } + + bytes_read = (S32)fread(buffer, 1, buffer_size, file.mFile); + if( bytes_read <= 0 ) + { + LL_WARNS("ReadXUI") << "Error while reading file " << filename << LL_ENDL; + XML_ParserFree( mParser ); + return false; + } + + mEmptyLeafNode.push_back(false); + + if( !XML_ParseBuffer(mParser, bytes_read, true ) ) + { + LL_WARNS("ReadXUI") << "Error while parsing file " << filename << LL_ENDL; + XML_ParserFree( mParser ); + return false; + } + + mEmptyLeafNode.pop_back(); + + XML_ParserFree( mParser ); + return true; +} + +void LLSimpleXUIParser::startElementHandler(void *userData, const char *name, const char **atts) +{ + LLSimpleXUIParser* self = reinterpret_cast(userData); + self->startElement(name, atts); +} + +void LLSimpleXUIParser::endElementHandler(void *userData, const char *name) +{ + LLSimpleXUIParser* self = reinterpret_cast(userData); + self->endElement(name); +} + +void LLSimpleXUIParser::characterDataHandler(void *userData, const char *s, int len) +{ + LLSimpleXUIParser* self = reinterpret_cast(userData); + self->characterData(s, len); +} + +void LLSimpleXUIParser::characterData(const char *s, int len) +{ + mTextContents += std::string(s, len); +} + +void LLSimpleXUIParser::startElement(const char *name, const char **atts) +{ + processText(); + + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("."); + + if (mElementCB) + { + LLInitParam::BaseBlock* blockp = mElementCB(*this, name); + if (blockp) + { + mOutputStack.push_back(std::make_pair(blockp, 0)); + } + } + + mOutputStack.back().second++; + S32 num_tokens_pushed = 0; + std::string child_name(name); + + if (mOutputStack.back().second == 1) + { // root node for this block + mScope.push_back(child_name); + } + else + { // compound attribute + if (child_name.find(".") == std::string::npos) + { + mNameStack.push_back(std::make_pair(child_name, true)); + num_tokens_pushed++; + mScope.push_back(child_name); + } + else + { + // parse out "dotted" name into individual tokens + tokenizer name_tokens(child_name, sep); + + tokenizer::iterator name_token_it = name_tokens.begin(); + if(name_token_it == name_tokens.end()) + { + return; + } + + // check for proper nesting + if(!mScope.empty() && *name_token_it != mScope.back()) + { + return; + } + + // now ignore first token + ++name_token_it; + + // copy remaining tokens on to our running token list + for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push) + { + mNameStack.push_back(std::make_pair(*token_to_push, true)); + num_tokens_pushed++; + } + mScope.push_back(mNameStack.back().first); + } + } + + // parent node is not empty + mEmptyLeafNode.back() = false; + // we are empty if we have no attributes + mEmptyLeafNode.push_back(atts[0] == NULL); + + mTokenSizeStack.push_back(num_tokens_pushed); + readAttributes(atts); + +} + +void LLSimpleXUIParser::endElement(const char *name) +{ + bool has_text = processText(); + + // no text, attributes, or children + if (!has_text && mEmptyLeafNode.back()) + { + // submit this as a valueless name (even though there might be text contents we haven't seen yet) + mCurAttributeValueBegin = NO_VALUE_MARKER; + mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently); + } + + if (--mOutputStack.back().second == 0) + { + if (mOutputStack.empty()) + { + LL_ERRS("ReadXUI") << "Parameter block output stack popped while empty." << LL_ENDL; + } + mOutputStack.pop_back(); + } + + S32 num_tokens_to_pop = mTokenSizeStack.back(); + mTokenSizeStack.pop_back(); + while(num_tokens_to_pop-- > 0) + { + mNameStack.pop_back(); + } + mScope.pop_back(); + mEmptyLeafNode.pop_back(); +} + +bool LLSimpleXUIParser::readAttributes(const char **atts) +{ + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("."); + + bool any_parsed = false; + for(S32 i = 0; atts[i] && atts[i+1]; i += 2 ) + { + std::string attribute_name(atts[i]); + mCurAttributeValueBegin = atts[i+1]; + + S32 num_tokens_pushed = 0; + tokenizer name_tokens(attribute_name, sep); + // copy remaining tokens on to our running token list + for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push) + { + mNameStack.push_back(std::make_pair(*token_to_push, true)); + num_tokens_pushed++; + } + + // child nodes are not necessarily valid attributes, so don't complain once we've recursed + any_parsed |= mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently); + + while(num_tokens_pushed-- > 0) + { + mNameStack.pop_back(); + } + } + return any_parsed; +} + +bool LLSimpleXUIParser::processText() +{ + if (!mTextContents.empty()) + { + LLStringUtil::trim(mTextContents); + if (!mTextContents.empty()) + { + mNameStack.push_back(std::make_pair(std::string("value"), true)); + mCurAttributeValueBegin = mTextContents.c_str(); + mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently); + mNameStack.pop_back(); + } + mTextContents.clear(); + return true; + } + return false; +} + +/*virtual*/ std::string LLSimpleXUIParser::getCurrentElementName() +{ + std::string full_name; + for (name_stack_t::iterator it = mNameStack.begin(); + it != mNameStack.end(); + ++it) + { + full_name += it->first + "."; // build up dotted names: "button.param.nestedparam." + } + + return full_name; +} + +void LLSimpleXUIParser::parserWarning(const std::string& message) +{ + std::string warning_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str()); + Parser::parserWarning(warning_msg); +} + +void LLSimpleXUIParser::parserError(const std::string& message) +{ + std::string error_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str()); + Parser::parserError(error_msg); +} + +bool LLSimpleXUIParser::readFlag(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return self.mCurAttributeValueBegin == NO_VALUE_MARKER; +} + +bool LLSimpleXUIParser::readBoolValue(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + if (!strcmp(self.mCurAttributeValueBegin, "true")) + { + *((bool*)val_ptr) = true; + return true; + } + else if (!strcmp(self.mCurAttributeValueBegin, "false")) + { + *((bool*)val_ptr) = false; + return true; + } + + return false; +} + +bool LLSimpleXUIParser::readStringValue(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + *((std::string*)val_ptr) = self.mCurAttributeValueBegin; + return true; +} + +bool LLSimpleXUIParser::readU8Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U8*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readS8Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S8*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readU16Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U16*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readS16Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S16*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readU32Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U32*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readS32Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S32*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readF32Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F32*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readF64Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F64*)val_ptr)]).full; +} + +bool LLSimpleXUIParser::readColor4Value(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + LLColor4 value; + + if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full) + { + *(LLColor4*)(val_ptr) = value; + return true; + } + return false; +} + +bool LLSimpleXUIParser::readUIColorValue(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + LLColor4 value; + LLUIColor* colorp = (LLUIColor*)val_ptr; + + if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full) + { + colorp->set(value); + return true; + } + return false; +} + +bool LLSimpleXUIParser::readUUIDValue(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + LLUUID temp_id; + // LLUUID::set is destructive, so use temporary value + if (temp_id.set(std::string(self.mCurAttributeValueBegin))) + { + *(LLUUID*)(val_ptr) = temp_id; + return true; + } + return false; +} + +bool LLSimpleXUIParser::readSDValue(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast(parser); + *((LLSD*)val_ptr) = LLSD(self.mCurAttributeValueBegin); + return true; +} diff --git a/indra/llui/llxyvector.h b/indra/llui/llxyvector.h index 9dbd5808eb..bc41213c13 100644 --- a/indra/llui/llxyvector.h +++ b/indra/llui/llxyvector.h @@ -1,122 +1,122 @@ -/** -* @file llxyvector.h -* @author Andrey Lihatskiy -* @brief Header file for LLXYVector -* -* $LicenseInfo:firstyear=2001&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2018, 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$ -*/ - -// A control that allows to set two related vector magnitudes by manipulating a single vector on a plane. - -#ifndef LL_LLXYVECTOR_H -#define LL_LLXYVECTOR_H - -#include "lluictrl.h" -#include "llpanel.h" -#include "lltextbox.h" -#include "lllineeditor.h" - -class LLXYVector - : public LLUICtrl -{ -public: - struct Params - : public LLInitParam::Block - { - Optional x_entry; - Optional y_entry; - Optional touch_area; - Optional border; - Optional edit_bar_height; - Optional padding; - Optional label_width; - Optional min_val_x; - Optional max_val_x; - Optional increment_x; - Optional min_val_y; - Optional max_val_y; - Optional increment_y; - Optional arrow_color; - Optional ghost_color; - Optional area_color; - Optional grid_color; - Optional logarithmic; - - Params(); - }; - - - virtual ~LLXYVector(); - /*virtual*/ bool postBuild(); - - virtual bool handleHover(S32 x, S32 y, MASK mask); - virtual bool handleMouseUp(S32 x, S32 y, MASK mask); - virtual bool handleMouseDown(S32 x, S32 y, MASK mask); - - virtual void draw(); - - virtual void setValue(const LLSD& value); - void setValue(F32 x, F32 y); - virtual LLSD getValue() const; - -protected: - friend class LLUICtrlFactory; - LLXYVector(const Params&); - void onEditChange(); - -protected: - LLTextBox* mXLabel; - LLTextBox* mYLabel; - LLLineEditor* mXEntry; - LLLineEditor* mYEntry; - LLPanel* mTouchArea; - LLViewBorder* mBorder; - -private: - void update(); - void setValueAndCommit(F32 x, F32 y); - - F32 mValueX; - F32 mValueY; - - F32 mMinValueX; - F32 mMaxValueX; - F32 mIncrementX; - F32 mMinValueY; - F32 mMaxValueY; - F32 mIncrementY; - - U32 mGhostX; - U32 mGhostY; - - LLUIColor mArrowColor; - LLUIColor mGhostColor; - LLUIColor mAreaColor; - LLUIColor mGridColor; - - bool mLogarithmic; - F32 mLogScaleX; - F32 mLogScaleY; -}; - -#endif - +/** +* @file llxyvector.h +* @author Andrey Lihatskiy +* @brief Header file for LLXYVector +* +* $LicenseInfo:firstyear=2001&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, 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$ +*/ + +// A control that allows to set two related vector magnitudes by manipulating a single vector on a plane. + +#ifndef LL_LLXYVECTOR_H +#define LL_LLXYVECTOR_H + +#include "lluictrl.h" +#include "llpanel.h" +#include "lltextbox.h" +#include "lllineeditor.h" + +class LLXYVector + : public LLUICtrl +{ +public: + struct Params + : public LLInitParam::Block + { + Optional x_entry; + Optional y_entry; + Optional touch_area; + Optional border; + Optional edit_bar_height; + Optional padding; + Optional label_width; + Optional min_val_x; + Optional max_val_x; + Optional increment_x; + Optional min_val_y; + Optional max_val_y; + Optional increment_y; + Optional arrow_color; + Optional ghost_color; + Optional area_color; + Optional grid_color; + Optional logarithmic; + + Params(); + }; + + + virtual ~LLXYVector(); + /*virtual*/ bool postBuild(); + + virtual bool handleHover(S32 x, S32 y, MASK mask); + virtual bool handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask); + + virtual void draw(); + + virtual void setValue(const LLSD& value); + void setValue(F32 x, F32 y); + virtual LLSD getValue() const; + +protected: + friend class LLUICtrlFactory; + LLXYVector(const Params&); + void onEditChange(); + +protected: + LLTextBox* mXLabel; + LLTextBox* mYLabel; + LLLineEditor* mXEntry; + LLLineEditor* mYEntry; + LLPanel* mTouchArea; + LLViewBorder* mBorder; + +private: + void update(); + void setValueAndCommit(F32 x, F32 y); + + F32 mValueX; + F32 mValueY; + + F32 mMinValueX; + F32 mMaxValueX; + F32 mIncrementX; + F32 mMinValueY; + F32 mMaxValueY; + F32 mIncrementY; + + U32 mGhostX; + U32 mGhostY; + + LLUIColor mArrowColor; + LLUIColor mGhostColor; + LLUIColor mAreaColor; + LLUIColor mGridColor; + + bool mLogarithmic; + F32 mLogScaleX; + F32 mLogScaleY; +}; + +#endif + diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 3f0b230a4f..088cdd675b 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -1,935 +1,935 @@ -/** - * @file llurlentry_test.cpp - * @author Martin Reddy - * @brief Unit tests for LLUrlEntry objects - * - * $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 "../llurlentry.h" -#include "../lluictrl.h" -//#include "llurlentry_stub.cpp" -#include "lltut.h" -#include "../lluicolortable.h" -#include "../llrender/lluiimage.h" -#include "../llmessage/llexperiencecache.h" - -#include - -#if LL_WINDOWS -// because something pulls in window and lldxdiag dependencies which in turn need wbemuuid.lib - #pragma comment(lib, "wbemuuid.lib") -#endif - - -// namespace LLExperienceCache -// { -// const LLSD& get( const LLUUID& key) -// { -// static LLSD boo; -// return boo; -// } -// -// void get( const LLUUID& key, callback_slot_t slot ){} -// -// } - -/*==========================================================================*| -typedef std::map settings_map_t; -settings_map_t LLUI::sSettingGroups; - -bool LLControlGroup::getBOOL(const std::string& name) -{ - return false; -} - -LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const -{ - return LLUIColor(); -} - -LLUIColor::LLUIColor() : mColorPtr(NULL) {} - -LLUIImage::LLUIImage(const std::string& name, LLPointer image) -{ -} - -LLUIImage::~LLUIImage() -{ -} - -//virtual -S32 LLUIImage::getWidth() const -{ - return 0; -} - -//virtual -S32 LLUIImage::getHeight() const -{ - return 0; -} -|*==========================================================================*/ - -namespace tut -{ - struct LLUrlEntryData - { - }; - - typedef test_group factory; - typedef factory::object object; -} - -namespace -{ - tut::factory tf("LLUrlEntry"); -} - -namespace tut -{ - void testRegex(const std::string &testname, LLUrlEntryBase &entry, - const char *text, const std::string &expected) - { - boost::regex regex = entry.getPattern(); - std::string url = ""; - boost::cmatch result; - bool found = boost::regex_search(text, result, regex); - if (found) - { - S32 start = static_cast(result[0].first - text); - S32 end = static_cast(result[0].second - text); - url = entry.getUrl(std::string(text+start, end-start)); - } - ensure_equals(testname, url, expected); - } - - void dummyCallback(const std::string &url, const std::string &label, const std::string& icon) - { - } - - void testLabel(const std::string &testname, LLUrlEntryBase &entry, - const char *text, const std::string &expected) - { - boost::regex regex = entry.getPattern(); - std::string label = ""; - boost::cmatch result; - bool found = boost::regex_search(text, result, regex); - if (found) - { - S32 start = static_cast(result[0].first - text); - S32 end = static_cast(result[0].second - text); - std::string url = std::string(text+start, end-start); - label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3)); - } - ensure_equals(testname, label, expected); - } - - void testLocation(const std::string &testname, LLUrlEntryBase &entry, - const char *text, const std::string &expected) - { - boost::regex regex = entry.getPattern(); - std::string location = ""; - boost::cmatch result; - bool found = boost::regex_search(text, result, regex); - if (found) - { - S32 start = static_cast(result[0].first - text); - S32 end = static_cast(result[0].second - text); - std::string url = std::string(text+start, end-start); - location = entry.getLocation(url); - } - ensure_equals(testname, location, expected); - } - - - template<> template<> - void object::test<1>() - { - // - // test LLUrlEntryHTTP - standard http Urls - // - LLUrlEntryHTTP url; - - testRegex("no valid url", url, - "htp://slurl.com/", - ""); - - testRegex("simple http (1)", url, - "http://slurl.com/", - "http://slurl.com/"); - - testRegex("simple http (2)", url, - "http://slurl.com", - "http://slurl.com"); - - testRegex("simple http (3)", url, - "http://slurl.com/about.php", - "http://slurl.com/about.php"); - - testRegex("simple https", url, - "https://slurl.com/about.php", - "https://slurl.com/about.php"); - - testRegex("http in text (1)", url, - "XX http://slurl.com/ XX", - "http://slurl.com/"); - - testRegex("http in text (2)", url, - "XX http://slurl.com/about.php XX", - "http://slurl.com/about.php"); - - testRegex("https in text", url, - "XX https://slurl.com/about.php XX", - "https://slurl.com/about.php"); - - testRegex("two http urls", url, - "XX http://slurl.com/about.php http://secondlife.com/ XX", - "http://slurl.com/about.php"); - - testRegex("http url with port and username", url, - "XX http://nobody@slurl.com:80/about.php http://secondlife.com/ XX", - "http://nobody@slurl.com:80/about.php"); - - testRegex("http url with port, username, and query string", url, - "XX http://nobody@slurl.com:80/about.php?title=hi%20there http://secondlife.com/ XX", - "http://nobody@slurl.com:80/about.php?title=hi%20there"); - - // note: terminating commas will be removed by LLUrlRegistry:findUrl() - testRegex("http url with commas in middle and terminating", url, - "XX http://slurl.com/?title=Hi,There, XX", - "http://slurl.com/?title=Hi,There,"); - - // note: terminating periods will be removed by LLUrlRegistry:findUrl() - testRegex("http url with periods in middle and terminating", url, - "XX http://slurl.com/index.php. XX", - "http://slurl.com/index.php."); - - // DEV-19842: Closing parenthesis ")" breaks urls - testRegex("http url with brackets (1)", url, - "XX http://en.wikipedia.org/wiki/JIRA_(software) XX", - "http://en.wikipedia.org/wiki/JIRA_(software)"); - - // DEV-19842: Closing parenthesis ")" breaks urls - testRegex("http url with brackets (2)", url, - "XX http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg XX", - "http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg"); - - // DEV-10353: URLs in chat log terminated incorrectly when newline in chat - testRegex("http url with newlines", url, - "XX\nhttp://www.secondlife.com/\nXX", - "http://www.secondlife.com/"); - - testRegex("http url without tld shouldn't be decorated (1)", url, - "http://test", - ""); - - testRegex("http url without tld shouldn't be decorated (2)", url, - "http://test .com", - ""); - } - - template<> template<> - void object::test<2>() - { - // - // test LLUrlEntryHTTPLabel - wiki-style http Urls with labels - // - LLUrlEntryHTTPLabel url; - - testRegex("invalid wiki url [1]", url, - "[http://www.example.org]", - ""); - - testRegex("invalid wiki url [2]", url, - "[http://www.example.org", - ""); - - testRegex("invalid wiki url [3]", url, - "[http://www.example.org Label", - ""); - - testRegex("example.org with label (spaces)", url, - "[http://www.example.org Text]", - "http://www.example.org"); - - testRegex("example.org with label (tabs)", url, - "[http://www.example.org\t Text]", - "http://www.example.org"); - - testRegex("SL http URL with label", url, - "[http://www.secondlife.com/ Second Life]", - "http://www.secondlife.com/"); - - testRegex("SL https URL with label", url, - "XXX [https://www.secondlife.com/ Second Life] YYY", - "https://www.secondlife.com/"); - - testRegex("SL http URL with label", url, - "[http://www.secondlife.com/?test=Hi%20There Second Life]", - "http://www.secondlife.com/?test=Hi%20There"); - } - - template<> template<> - void object::test<3>() - { - // - // test LLUrlEntrySLURL - second life URLs - // - LLUrlEntrySLURL url; - - testRegex("no valid slurl [1]", url, - "htp://slurl.com/secondlife/Ahern/50/50/50/", - ""); - - testRegex("no valid slurl [2]", url, - "http://slurl.com/secondlife/", - ""); - - testRegex("no valid slurl [3]", url, - "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/", - ""); - - testRegex("Ahern (50,50,50) [1]", url, - "http://slurl.com/secondlife/Ahern/50/50/50/", - "http://slurl.com/secondlife/Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [2]", url, - "XXX http://slurl.com/secondlife/Ahern/50/50/50/ XXX", - "http://slurl.com/secondlife/Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [3]", url, - "XXX http://slurl.com/secondlife/Ahern/50/50/50 XXX", - "http://slurl.com/secondlife/Ahern/50/50/50"); - - testRegex("Ahern (50,50,50) multicase", url, - "XXX http://SLUrl.com/SecondLife/Ahern/50/50/50/ XXX", - "http://SLUrl.com/SecondLife/Ahern/50/50/50/"); - - testRegex("Ahern (50,50) [1]", url, - "XXX http://slurl.com/secondlife/Ahern/50/50/ XXX", - "http://slurl.com/secondlife/Ahern/50/50/"); - - testRegex("Ahern (50,50) [2]", url, - "XXX http://slurl.com/secondlife/Ahern/50/50 XXX", - "http://slurl.com/secondlife/Ahern/50/50"); - - testRegex("Ahern (50)", url, - "XXX http://slurl.com/secondlife/Ahern/50 XXX", - "http://slurl.com/secondlife/Ahern/50"); - - testRegex("Ahern", url, - "XXX http://slurl.com/secondlife/Ahern/ XXX", - "http://slurl.com/secondlife/Ahern/"); - - testRegex("Ahern SLURL with title", url, - "XXX http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX", - "http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!"); - - testRegex("Ahern SLURL with msg", url, - "XXX http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here. XXX", - "http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here."); - - // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat - testRegex("SLURL with brackets", url, - "XXX http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30 XXX", - "http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30"); - - // DEV-35459: SLURLs and teleport Links not parsed properly - testRegex("SLURL with quote", url, - "XXX http://slurl.com/secondlife/A'ksha%20Oasis/41/166/701 XXX", - "http://slurl.com/secondlife/A%27ksha%20Oasis/41/166/701"); - } - - template<> template<> - void object::test<4>() - { - // - // test LLUrlEntryAgent - secondlife://app/agent Urls - // - LLUrlEntryAgent url; - - testRegex("Invalid Agent Url", url, - "secondlife:///app/agent/0e346d8b-4433-4d66-XXXX-fd37083abc4c/about", - ""); - - testRegex("Agent Url ", url, - "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", - "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - testRegex("Agent Url in text", url, - "XXX secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about XXX", - "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - testRegex("Agent Url multicase", url, - "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX", - "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About"); - - testRegex("Agent Url alternate command", url, - "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar", - "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar"); - - testRegex("Standalone Agent Url ", url, - "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", - "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - testRegex("Standalone Agent Url Multicase with Text", url, - "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", - "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - } - - template<> template<> - void object::test<5>() - { - // - // test LLUrlEntryGroup - secondlife://app/group Urls - // - LLUrlEntryGroup url; - - testRegex("Invalid Group Url", url, - "secondlife:///app/group/00005ff3-4044-c79f-XXXX-fb28ae0df991/about", - ""); - - testRegex("Group Url ", url, - "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about", - "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about"); - - testRegex("Group Url ", url, - "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect", - "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect"); - - testRegex("Group Url in text", url, - "XXX secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about XXX", - "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about"); - - testRegex("Group Url multicase", url, - "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX", - "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About"); - - testRegex("Standalone Group Url ", url, - "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", - "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - testRegex("Standalone Group Url Multicase ith Text", url, - "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", - "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - } - - template<> template<> - void object::test<6>() - { - // - // test LLUrlEntryPlace - secondlife:// URLs - // - LLUrlEntryPlace url; - - testRegex("no valid slurl [1]", url, - "secondlife://Ahern/FOO/50/", - ""); - - testRegex("Ahern (50,50,50) [1]", url, - "secondlife://Ahern/50/50/50/", - "secondlife://Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [2]", url, - "XXX secondlife://Ahern/50/50/50/ XXX", - "secondlife://Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [3]", url, - "XXX secondlife://Ahern/50/50/50 XXX", - "secondlife://Ahern/50/50/50"); - - testRegex("Ahern (50,50,50) multicase", url, - "XXX SecondLife://Ahern/50/50/50/ XXX", - "SecondLife://Ahern/50/50/50/"); - - testRegex("Ahern (50,50) [1]", url, - "XXX secondlife://Ahern/50/50/ XXX", - "secondlife://Ahern/50/50/"); - - testRegex("Ahern (50,50) [2]", url, - "XXX secondlife://Ahern/50/50 XXX", - "secondlife://Ahern/50/50"); - - // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat - testRegex("SLURL with brackets", url, - "XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX", - "secondlife://Burning%20Life%20(Hyper)/27/210/30"); - - // DEV-35459: SLURLs and teleport Links not parsed properly - testRegex("SLURL with quote", url, - "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX", - "secondlife://A%27ksha%20Oasis/41/166/701"); - - testRegex("Standalone All Hands (50,50) [2] with text", url, - "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX", - "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50"); - } - - template<> template<> - void object::test<7>() - { - // - // test LLUrlEntryParcel - secondlife://app/parcel Urls - // - LLUrlEntryParcel url; - - testRegex("Invalid Classified Url", url, - "secondlife:///app/parcel/0000060e-4b39-e00b-XXXX-d98b1934e3a8/about", - ""); - - testRegex("Classified Url ", url, - "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about", - "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about"); - - testRegex("Classified Url in text", url, - "XXX secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about XXX", - "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about"); - - testRegex("Classified Url multicase", url, - "XXX secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About XXX", - "secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About"); - } - template<> template<> - void object::test<8>() - { - // - // test LLUrlEntryTeleport - secondlife://app/teleport URLs - // - LLUrlEntryTeleport url; - - testRegex("no valid teleport [1]", url, - "http://slurl.com/secondlife/Ahern/50/50/50/", - ""); - - testRegex("no valid teleport [2]", url, - "secondlife:///app/teleport/", - ""); - - testRegex("no valid teleport [3]", url, - "second-life:///app/teleport/Ahern/50/50/50/", - ""); - - testRegex("no valid teleport [3]", url, - "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/", - ""); - - testRegex("Ahern (50,50,50) [1]", url, - "secondlife:///app/teleport/Ahern/50/50/50/", - "secondlife:///app/teleport/Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [2]", url, - "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX", - "secondlife:///app/teleport/Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [3]", url, - "XXX secondlife:///app/teleport/Ahern/50/50/50 XXX", - "secondlife:///app/teleport/Ahern/50/50/50"); - - testRegex("Ahern (50,50,50) multicase", url, - "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX", - "secondlife:///app/teleport/Ahern/50/50/50/"); - - testRegex("Ahern (50,50) [1]", url, - "XXX secondlife:///app/teleport/Ahern/50/50/ XXX", - "secondlife:///app/teleport/Ahern/50/50/"); - - testRegex("Ahern (50,50) [2]", url, - "XXX secondlife:///app/teleport/Ahern/50/50 XXX", - "secondlife:///app/teleport/Ahern/50/50"); - - testRegex("Ahern (50)", url, - "XXX secondlife:///app/teleport/Ahern/50 XXX", - "secondlife:///app/teleport/Ahern/50"); - - testRegex("Ahern", url, - "XXX secondlife:///app/teleport/Ahern/ XXX", - "secondlife:///app/teleport/Ahern/"); - - testRegex("Ahern teleport with title", url, - "XXX secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX", - "secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!"); - - testRegex("Ahern teleport with msg", url, - "XXX secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here. XXX", - "secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here."); - - // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat - testRegex("Teleport with brackets", url, - "XXX secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30 XXX", - "secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30"); - - // DEV-35459: SLURLs and teleport Links not parsed properly - testRegex("Teleport url with quote", url, - "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX", - "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701"); - - testRegex("Standalone All Hands", url, - "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX", - "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50"); - } - - template<> template<> - void object::test<9>() - { - // - // test LLUrlEntrySL - general secondlife:// URLs - // - LLUrlEntrySL url; - - testRegex("no valid slapp [1]", url, - "http:///app/", - ""); - - testRegex("valid slapp [1]", url, - "secondlife:///app/", - "secondlife:///app/"); - - testRegex("valid slapp [2]", url, - "secondlife:///app/teleport/Ahern/50/50/50/", - "secondlife:///app/teleport/Ahern/50/50/50/"); - - testRegex("valid slapp [3]", url, - "secondlife:///app/foo", - "secondlife:///app/foo"); - - testRegex("valid slapp [4]", url, - "secondlife:///APP/foo?title=Hi%20There", - "secondlife:///APP/foo?title=Hi%20There"); - - testRegex("valid slapp [5]", url, - "secondlife://host/app/", - "secondlife://host/app/"); - - testRegex("valid slapp [6]", url, - "secondlife://host:8080/foo/bar", - "secondlife://host:8080/foo/bar"); - } - - template<> template<> - void object::test<10>() - { - // - // test LLUrlEntrySLLabel - general secondlife:// URLs with labels - // - LLUrlEntrySLLabel url; - - testRegex("invalid wiki url [1]", url, - "[secondlife:///app/]", - ""); - - testRegex("invalid wiki url [2]", url, - "[secondlife:///app/", - ""); - - testRegex("invalid wiki url [3]", url, - "[secondlife:///app/ Label", - ""); - - testRegex("agent slurl with label (spaces)", url, - "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about Text]", - "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - testRegex("agent slurl with label (tabs)", url, - "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about\t Text]", - "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - testRegex("agent slurl with label", url, - "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about FirstName LastName]", - "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); - - testRegex("teleport slurl with label", url, - "XXX [secondlife:///app/teleport/Ahern/50/50/50/ Teleport to Ahern] YYY", - "secondlife:///app/teleport/Ahern/50/50/50/"); - } - - template<> template<> - void object::test<11>() - { - // - // test LLUrlEntryNoLink - turn off hyperlinking - // - LLUrlEntryNoLink url; - - testRegex(" [1]", url, - "google.com", - "google.com"); - - testRegex(" [2]", url, - "google.com", - ""); - - testRegex(" [3]", url, - "google.com", - ""); - - testRegex(" [4]", url, - "Hello World", - "Hello World"); - - testRegex(" [5]", url, - "My Object", - "My Object"); - } - - template<> template<> - void object::test<12>() - { - // - // test LLUrlEntryRegion - secondlife:///app/region/ URLs - // - LLUrlEntryRegion url; - - // Regex tests. - testRegex("no valid region", url, - "secondlife:///app/region/", - ""); - - testRegex("invalid coords", url, - "secondlife:///app/region/Korea2/a/b/c", - "secondlife:///app/region/Korea2/"); // don't count invalid coords - - testRegex("Ahern (50,50,50) [1]", url, - "secondlife:///app/region/Ahern/50/50/50/", - "secondlife:///app/region/Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [2]", url, - "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", - "secondlife:///app/region/Ahern/50/50/50/"); - - testRegex("Ahern (50,50,50) [3]", url, - "XXX secondlife:///app/region/Ahern/50/50/50 XXX", - "secondlife:///app/region/Ahern/50/50/50"); - - testRegex("Ahern (50,50,50) multicase", url, - "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", - "secondlife:///app/region/Ahern/50/50/50/"); - - testRegex("Ahern (50,50) [1]", url, - "XXX secondlife:///app/region/Ahern/50/50/ XXX", - "secondlife:///app/region/Ahern/50/50/"); - - testRegex("Ahern (50,50) [2]", url, - "XXX secondlife:///app/region/Ahern/50/50 XXX", - "secondlife:///app/region/Ahern/50/50"); - - // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat - testRegex("Region with brackets", url, - "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX", - "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30"); - - // Rendering tests. - testLabel("Render /app/region/Ahern/50/50/50/", url, - "secondlife:///app/region/Ahern/50/50/50/", - "Ahern (50,50,50)"); - - testLabel("Render /app/region/Ahern/50/50/50", url, - "secondlife:///app/region/Ahern/50/50/50", - "Ahern (50,50,50)"); - - testLabel("Render /app/region/Ahern/50/50/", url, - "secondlife:///app/region/Ahern/50/50/", - "Ahern (50,50)"); - - testLabel("Render /app/region/Ahern/50/50", url, - "secondlife:///app/region/Ahern/50/50", - "Ahern (50,50)"); - - testLabel("Render /app/region/Ahern/50/", url, - "secondlife:///app/region/Ahern/50/", - "Ahern (50)"); - - testLabel("Render /app/region/Ahern/50", url, - "secondlife:///app/region/Ahern/50", - "Ahern (50)"); - - testLabel("Render /app/region/Ahern/", url, - "secondlife:///app/region/Ahern/", - "Ahern"); - - testLabel("Render /app/region/Ahern/ within context", url, - "XXX secondlife:///app/region/Ahern/ XXX", - "Ahern"); - - testLabel("Render /app/region/Ahern", url, - "secondlife:///app/region/Ahern", - "Ahern"); - - testLabel("Render /app/region/Ahern within context", url, - "XXX secondlife:///app/region/Ahern XXX", - "Ahern"); - - testLabel("Render /app/region/Product%20Engine/", url, - "secondlife:///app/region/Product%20Engine/", - "Product Engine"); - - testLabel("Render /app/region/Product%20Engine", url, - "secondlife:///app/region/Product%20Engine", - "Product Engine"); - - // Location parsing texts. - testLocation("Location /app/region/Ahern/50/50/50/", url, - "secondlife:///app/region/Ahern/50/50/50/", - "Ahern"); - - testLocation("Location /app/region/Product%20Engine", url, - "secondlife:///app/region/Product%20Engine", - "Product Engine"); - } - - template<> template<> - void object::test<13>() - { - // - // test LLUrlEntryemail - general emails - // - LLUrlEntryEmail url; - - // Regex tests. - testRegex("match e-mail addresses", url, - "test@lindenlab.com", - "mailto:test@lindenlab.com"); - - testRegex("match e-mail addresses with mailto: prefix", url, - "mailto:test@lindenlab.com", - "mailto:test@lindenlab.com"); - - testRegex("match e-mail addresses with different domains", url, - "test@foo.org.us", - "mailto:test@foo.org.us"); - - testRegex("match e-mail addresses with different domains", url, - "test@foo.bar", - "mailto:test@foo.bar"); - - testRegex("don't match incorrect e-mail addresses", url, - "test @foo.com", - ""); - - testRegex("don't match incorrect e-mail addresses", url, - "test@ foo.com", - ""); - } - - template<> template<> - void object::test<14>() - { - // - // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com/* and http://*lindenlab.com/* urls - // - LLUrlEntrySecondlifeURL url; - - testRegex("match urls with protocol", url, - "this url should match http://lindenlab.com/products/second-life", - "http://lindenlab.com/products/second-life"); - - testRegex("match urls with protocol", url, - "search something https://marketplace.secondlife.com/products/search on marketplace and test the https", - "https://marketplace.secondlife.com/products/search"); - - testRegex("match HTTPS urls with port", url, - "let's specify some port https://secondlife.com:888/status", - "https://secondlife.com:888/status"); - - testRegex("don't match HTTP urls with port", url, - "let's specify some port for HTTP http://secondlife.com:888/status", - ""); - - testRegex("don't match urls w/o protocol", url, - "looks like an url something www.marketplace.secondlife.com/products but no https prefix", - ""); - - testRegex("but with a protocol www is fine", url, - "so let's add a protocol https://www.marketplace.secondlife.com:8888/products", - "https://www.marketplace.secondlife.com:8888/products"); - - testRegex("don't match urls w/o protocol", url, - "and even no www something secondlife.com/status", - ""); - } - - template<> template<> - void object::test<15>() - { - // - // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com and http://*lindenlab.com urls - // - - LLUrlEntrySimpleSecondlifeURL url; - - testRegex("match urls with a protocol", url, - "this url should match http://lindenlab.com", - "http://lindenlab.com"); - - testRegex("match urls with a protocol", url, - "search something https://marketplace.secondlife.com on marketplace and test the https", - "https://marketplace.secondlife.com"); - - testRegex("don't match urls w/o protocol", url, - "looks like an url something www.marketplace.secondlife.com but no https prefix", - ""); - - testRegex("but with a protocol www is fine", url, - "so let's add a protocol http://www.marketplace.secondlife.com", - "http://www.marketplace.secondlife.com"); - - testRegex("don't match urls w/o protocol", url, - "and even no www something lindenlab.com", - ""); - } - - template<> template<> - void object::test<16>() - { - // - // test LLUrlEntryIPv6 - // - LLUrlEntryIPv6 url; - - // Regex tests. - testRegex("match urls with a protocol", url, - "this url should match http://[::1]", - "http://[::1]"); - - testRegex("match urls with a protocol and query", url, - "this url should match http://[::1]/file.mp3", - "http://[::1]/file.mp3"); - - testRegex("match urls with a protocol", url, - "this url should match http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]", - "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"); - - testRegex("match urls with port", url, - "let's specify some port http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080", - "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080"); - - testRegex("don't match urls w/o protocol", url, - "looks like an url something [2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d] but no https prefix", - ""); - - testRegex("don't match incorrect urls", url, - "http://[ 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d ]", - ""); - } -} +/** + * @file llurlentry_test.cpp + * @author Martin Reddy + * @brief Unit tests for LLUrlEntry objects + * + * $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 "../llurlentry.h" +#include "../lluictrl.h" +//#include "llurlentry_stub.cpp" +#include "lltut.h" +#include "../lluicolortable.h" +#include "../llrender/lluiimage.h" +#include "../llmessage/llexperiencecache.h" + +#include + +#if LL_WINDOWS +// because something pulls in window and lldxdiag dependencies which in turn need wbemuuid.lib + #pragma comment(lib, "wbemuuid.lib") +#endif + + +// namespace LLExperienceCache +// { +// const LLSD& get( const LLUUID& key) +// { +// static LLSD boo; +// return boo; +// } +// +// void get( const LLUUID& key, callback_slot_t slot ){} +// +// } + +/*==========================================================================*| +typedef std::map settings_map_t; +settings_map_t LLUI::sSettingGroups; + +bool LLControlGroup::getBOOL(const std::string& name) +{ + return false; +} + +LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const +{ + return LLUIColor(); +} + +LLUIColor::LLUIColor() : mColorPtr(NULL) {} + +LLUIImage::LLUIImage(const std::string& name, LLPointer image) +{ +} + +LLUIImage::~LLUIImage() +{ +} + +//virtual +S32 LLUIImage::getWidth() const +{ + return 0; +} + +//virtual +S32 LLUIImage::getHeight() const +{ + return 0; +} +|*==========================================================================*/ + +namespace tut +{ + struct LLUrlEntryData + { + }; + + typedef test_group factory; + typedef factory::object object; +} + +namespace +{ + tut::factory tf("LLUrlEntry"); +} + +namespace tut +{ + void testRegex(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) + { + boost::regex regex = entry.getPattern(); + std::string url = ""; + boost::cmatch result; + bool found = boost::regex_search(text, result, regex); + if (found) + { + S32 start = static_cast(result[0].first - text); + S32 end = static_cast(result[0].second - text); + url = entry.getUrl(std::string(text+start, end-start)); + } + ensure_equals(testname, url, expected); + } + + void dummyCallback(const std::string &url, const std::string &label, const std::string& icon) + { + } + + void testLabel(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) + { + boost::regex regex = entry.getPattern(); + std::string label = ""; + boost::cmatch result; + bool found = boost::regex_search(text, result, regex); + if (found) + { + S32 start = static_cast(result[0].first - text); + S32 end = static_cast(result[0].second - text); + std::string url = std::string(text+start, end-start); + label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3)); + } + ensure_equals(testname, label, expected); + } + + void testLocation(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) + { + boost::regex regex = entry.getPattern(); + std::string location = ""; + boost::cmatch result; + bool found = boost::regex_search(text, result, regex); + if (found) + { + S32 start = static_cast(result[0].first - text); + S32 end = static_cast(result[0].second - text); + std::string url = std::string(text+start, end-start); + location = entry.getLocation(url); + } + ensure_equals(testname, location, expected); + } + + + template<> template<> + void object::test<1>() + { + // + // test LLUrlEntryHTTP - standard http Urls + // + LLUrlEntryHTTP url; + + testRegex("no valid url", url, + "htp://slurl.com/", + ""); + + testRegex("simple http (1)", url, + "http://slurl.com/", + "http://slurl.com/"); + + testRegex("simple http (2)", url, + "http://slurl.com", + "http://slurl.com"); + + testRegex("simple http (3)", url, + "http://slurl.com/about.php", + "http://slurl.com/about.php"); + + testRegex("simple https", url, + "https://slurl.com/about.php", + "https://slurl.com/about.php"); + + testRegex("http in text (1)", url, + "XX http://slurl.com/ XX", + "http://slurl.com/"); + + testRegex("http in text (2)", url, + "XX http://slurl.com/about.php XX", + "http://slurl.com/about.php"); + + testRegex("https in text", url, + "XX https://slurl.com/about.php XX", + "https://slurl.com/about.php"); + + testRegex("two http urls", url, + "XX http://slurl.com/about.php http://secondlife.com/ XX", + "http://slurl.com/about.php"); + + testRegex("http url with port and username", url, + "XX http://nobody@slurl.com:80/about.php http://secondlife.com/ XX", + "http://nobody@slurl.com:80/about.php"); + + testRegex("http url with port, username, and query string", url, + "XX http://nobody@slurl.com:80/about.php?title=hi%20there http://secondlife.com/ XX", + "http://nobody@slurl.com:80/about.php?title=hi%20there"); + + // note: terminating commas will be removed by LLUrlRegistry:findUrl() + testRegex("http url with commas in middle and terminating", url, + "XX http://slurl.com/?title=Hi,There, XX", + "http://slurl.com/?title=Hi,There,"); + + // note: terminating periods will be removed by LLUrlRegistry:findUrl() + testRegex("http url with periods in middle and terminating", url, + "XX http://slurl.com/index.php. XX", + "http://slurl.com/index.php."); + + // DEV-19842: Closing parenthesis ")" breaks urls + testRegex("http url with brackets (1)", url, + "XX http://en.wikipedia.org/wiki/JIRA_(software) XX", + "http://en.wikipedia.org/wiki/JIRA_(software)"); + + // DEV-19842: Closing parenthesis ")" breaks urls + testRegex("http url with brackets (2)", url, + "XX http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg XX", + "http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg"); + + // DEV-10353: URLs in chat log terminated incorrectly when newline in chat + testRegex("http url with newlines", url, + "XX\nhttp://www.secondlife.com/\nXX", + "http://www.secondlife.com/"); + + testRegex("http url without tld shouldn't be decorated (1)", url, + "http://test", + ""); + + testRegex("http url without tld shouldn't be decorated (2)", url, + "http://test .com", + ""); + } + + template<> template<> + void object::test<2>() + { + // + // test LLUrlEntryHTTPLabel - wiki-style http Urls with labels + // + LLUrlEntryHTTPLabel url; + + testRegex("invalid wiki url [1]", url, + "[http://www.example.org]", + ""); + + testRegex("invalid wiki url [2]", url, + "[http://www.example.org", + ""); + + testRegex("invalid wiki url [3]", url, + "[http://www.example.org Label", + ""); + + testRegex("example.org with label (spaces)", url, + "[http://www.example.org Text]", + "http://www.example.org"); + + testRegex("example.org with label (tabs)", url, + "[http://www.example.org\t Text]", + "http://www.example.org"); + + testRegex("SL http URL with label", url, + "[http://www.secondlife.com/ Second Life]", + "http://www.secondlife.com/"); + + testRegex("SL https URL with label", url, + "XXX [https://www.secondlife.com/ Second Life] YYY", + "https://www.secondlife.com/"); + + testRegex("SL http URL with label", url, + "[http://www.secondlife.com/?test=Hi%20There Second Life]", + "http://www.secondlife.com/?test=Hi%20There"); + } + + template<> template<> + void object::test<3>() + { + // + // test LLUrlEntrySLURL - second life URLs + // + LLUrlEntrySLURL url; + + testRegex("no valid slurl [1]", url, + "htp://slurl.com/secondlife/Ahern/50/50/50/", + ""); + + testRegex("no valid slurl [2]", url, + "http://slurl.com/secondlife/", + ""); + + testRegex("no valid slurl [3]", url, + "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/", + ""); + + testRegex("Ahern (50,50,50) [1]", url, + "http://slurl.com/secondlife/Ahern/50/50/50/", + "http://slurl.com/secondlife/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [2]", url, + "XXX http://slurl.com/secondlife/Ahern/50/50/50/ XXX", + "http://slurl.com/secondlife/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [3]", url, + "XXX http://slurl.com/secondlife/Ahern/50/50/50 XXX", + "http://slurl.com/secondlife/Ahern/50/50/50"); + + testRegex("Ahern (50,50,50) multicase", url, + "XXX http://SLUrl.com/SecondLife/Ahern/50/50/50/ XXX", + "http://SLUrl.com/SecondLife/Ahern/50/50/50/"); + + testRegex("Ahern (50,50) [1]", url, + "XXX http://slurl.com/secondlife/Ahern/50/50/ XXX", + "http://slurl.com/secondlife/Ahern/50/50/"); + + testRegex("Ahern (50,50) [2]", url, + "XXX http://slurl.com/secondlife/Ahern/50/50 XXX", + "http://slurl.com/secondlife/Ahern/50/50"); + + testRegex("Ahern (50)", url, + "XXX http://slurl.com/secondlife/Ahern/50 XXX", + "http://slurl.com/secondlife/Ahern/50"); + + testRegex("Ahern", url, + "XXX http://slurl.com/secondlife/Ahern/ XXX", + "http://slurl.com/secondlife/Ahern/"); + + testRegex("Ahern SLURL with title", url, + "XXX http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX", + "http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!"); + + testRegex("Ahern SLURL with msg", url, + "XXX http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here. XXX", + "http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here."); + + // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat + testRegex("SLURL with brackets", url, + "XXX http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30 XXX", + "http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30"); + + // DEV-35459: SLURLs and teleport Links not parsed properly + testRegex("SLURL with quote", url, + "XXX http://slurl.com/secondlife/A'ksha%20Oasis/41/166/701 XXX", + "http://slurl.com/secondlife/A%27ksha%20Oasis/41/166/701"); + } + + template<> template<> + void object::test<4>() + { + // + // test LLUrlEntryAgent - secondlife://app/agent Urls + // + LLUrlEntryAgent url; + + testRegex("Invalid Agent Url", url, + "secondlife:///app/agent/0e346d8b-4433-4d66-XXXX-fd37083abc4c/about", + ""); + + testRegex("Agent Url ", url, + "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", + "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Agent Url in text", url, + "XXX secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about XXX", + "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Agent Url multicase", url, + "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX", + "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About"); + + testRegex("Agent Url alternate command", url, + "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar", + "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar"); + + testRegex("Standalone Agent Url ", url, + "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", + "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Standalone Agent Url Multicase with Text", url, + "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", + "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + } + + template<> template<> + void object::test<5>() + { + // + // test LLUrlEntryGroup - secondlife://app/group Urls + // + LLUrlEntryGroup url; + + testRegex("Invalid Group Url", url, + "secondlife:///app/group/00005ff3-4044-c79f-XXXX-fb28ae0df991/about", + ""); + + testRegex("Group Url ", url, + "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about", + "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about"); + + testRegex("Group Url ", url, + "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect", + "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect"); + + testRegex("Group Url in text", url, + "XXX secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about XXX", + "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about"); + + testRegex("Group Url multicase", url, + "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX", + "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About"); + + testRegex("Standalone Group Url ", url, + "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", + "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Standalone Group Url Multicase ith Text", url, + "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", + "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + } + + template<> template<> + void object::test<6>() + { + // + // test LLUrlEntryPlace - secondlife:// URLs + // + LLUrlEntryPlace url; + + testRegex("no valid slurl [1]", url, + "secondlife://Ahern/FOO/50/", + ""); + + testRegex("Ahern (50,50,50) [1]", url, + "secondlife://Ahern/50/50/50/", + "secondlife://Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [2]", url, + "XXX secondlife://Ahern/50/50/50/ XXX", + "secondlife://Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [3]", url, + "XXX secondlife://Ahern/50/50/50 XXX", + "secondlife://Ahern/50/50/50"); + + testRegex("Ahern (50,50,50) multicase", url, + "XXX SecondLife://Ahern/50/50/50/ XXX", + "SecondLife://Ahern/50/50/50/"); + + testRegex("Ahern (50,50) [1]", url, + "XXX secondlife://Ahern/50/50/ XXX", + "secondlife://Ahern/50/50/"); + + testRegex("Ahern (50,50) [2]", url, + "XXX secondlife://Ahern/50/50 XXX", + "secondlife://Ahern/50/50"); + + // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat + testRegex("SLURL with brackets", url, + "XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX", + "secondlife://Burning%20Life%20(Hyper)/27/210/30"); + + // DEV-35459: SLURLs and teleport Links not parsed properly + testRegex("SLURL with quote", url, + "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX", + "secondlife://A%27ksha%20Oasis/41/166/701"); + + testRegex("Standalone All Hands (50,50) [2] with text", url, + "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX", + "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50"); + } + + template<> template<> + void object::test<7>() + { + // + // test LLUrlEntryParcel - secondlife://app/parcel Urls + // + LLUrlEntryParcel url; + + testRegex("Invalid Classified Url", url, + "secondlife:///app/parcel/0000060e-4b39-e00b-XXXX-d98b1934e3a8/about", + ""); + + testRegex("Classified Url ", url, + "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about", + "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about"); + + testRegex("Classified Url in text", url, + "XXX secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about XXX", + "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about"); + + testRegex("Classified Url multicase", url, + "XXX secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About XXX", + "secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About"); + } + template<> template<> + void object::test<8>() + { + // + // test LLUrlEntryTeleport - secondlife://app/teleport URLs + // + LLUrlEntryTeleport url; + + testRegex("no valid teleport [1]", url, + "http://slurl.com/secondlife/Ahern/50/50/50/", + ""); + + testRegex("no valid teleport [2]", url, + "secondlife:///app/teleport/", + ""); + + testRegex("no valid teleport [3]", url, + "second-life:///app/teleport/Ahern/50/50/50/", + ""); + + testRegex("no valid teleport [3]", url, + "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/", + ""); + + testRegex("Ahern (50,50,50) [1]", url, + "secondlife:///app/teleport/Ahern/50/50/50/", + "secondlife:///app/teleport/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [2]", url, + "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX", + "secondlife:///app/teleport/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [3]", url, + "XXX secondlife:///app/teleport/Ahern/50/50/50 XXX", + "secondlife:///app/teleport/Ahern/50/50/50"); + + testRegex("Ahern (50,50,50) multicase", url, + "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX", + "secondlife:///app/teleport/Ahern/50/50/50/"); + + testRegex("Ahern (50,50) [1]", url, + "XXX secondlife:///app/teleport/Ahern/50/50/ XXX", + "secondlife:///app/teleport/Ahern/50/50/"); + + testRegex("Ahern (50,50) [2]", url, + "XXX secondlife:///app/teleport/Ahern/50/50 XXX", + "secondlife:///app/teleport/Ahern/50/50"); + + testRegex("Ahern (50)", url, + "XXX secondlife:///app/teleport/Ahern/50 XXX", + "secondlife:///app/teleport/Ahern/50"); + + testRegex("Ahern", url, + "XXX secondlife:///app/teleport/Ahern/ XXX", + "secondlife:///app/teleport/Ahern/"); + + testRegex("Ahern teleport with title", url, + "XXX secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX", + "secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!"); + + testRegex("Ahern teleport with msg", url, + "XXX secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here. XXX", + "secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here."); + + // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat + testRegex("Teleport with brackets", url, + "XXX secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30 XXX", + "secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30"); + + // DEV-35459: SLURLs and teleport Links not parsed properly + testRegex("Teleport url with quote", url, + "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX", + "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701"); + + testRegex("Standalone All Hands", url, + "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX", + "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50"); + } + + template<> template<> + void object::test<9>() + { + // + // test LLUrlEntrySL - general secondlife:// URLs + // + LLUrlEntrySL url; + + testRegex("no valid slapp [1]", url, + "http:///app/", + ""); + + testRegex("valid slapp [1]", url, + "secondlife:///app/", + "secondlife:///app/"); + + testRegex("valid slapp [2]", url, + "secondlife:///app/teleport/Ahern/50/50/50/", + "secondlife:///app/teleport/Ahern/50/50/50/"); + + testRegex("valid slapp [3]", url, + "secondlife:///app/foo", + "secondlife:///app/foo"); + + testRegex("valid slapp [4]", url, + "secondlife:///APP/foo?title=Hi%20There", + "secondlife:///APP/foo?title=Hi%20There"); + + testRegex("valid slapp [5]", url, + "secondlife://host/app/", + "secondlife://host/app/"); + + testRegex("valid slapp [6]", url, + "secondlife://host:8080/foo/bar", + "secondlife://host:8080/foo/bar"); + } + + template<> template<> + void object::test<10>() + { + // + // test LLUrlEntrySLLabel - general secondlife:// URLs with labels + // + LLUrlEntrySLLabel url; + + testRegex("invalid wiki url [1]", url, + "[secondlife:///app/]", + ""); + + testRegex("invalid wiki url [2]", url, + "[secondlife:///app/", + ""); + + testRegex("invalid wiki url [3]", url, + "[secondlife:///app/ Label", + ""); + + testRegex("agent slurl with label (spaces)", url, + "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about Text]", + "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("agent slurl with label (tabs)", url, + "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about\t Text]", + "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("agent slurl with label", url, + "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about FirstName LastName]", + "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("teleport slurl with label", url, + "XXX [secondlife:///app/teleport/Ahern/50/50/50/ Teleport to Ahern] YYY", + "secondlife:///app/teleport/Ahern/50/50/50/"); + } + + template<> template<> + void object::test<11>() + { + // + // test LLUrlEntryNoLink - turn off hyperlinking + // + LLUrlEntryNoLink url; + + testRegex(" [1]", url, + "google.com", + "google.com"); + + testRegex(" [2]", url, + "google.com", + ""); + + testRegex(" [3]", url, + "google.com", + ""); + + testRegex(" [4]", url, + "Hello World", + "Hello World"); + + testRegex(" [5]", url, + "My Object", + "My Object"); + } + + template<> template<> + void object::test<12>() + { + // + // test LLUrlEntryRegion - secondlife:///app/region/ URLs + // + LLUrlEntryRegion url; + + // Regex tests. + testRegex("no valid region", url, + "secondlife:///app/region/", + ""); + + testRegex("invalid coords", url, + "secondlife:///app/region/Korea2/a/b/c", + "secondlife:///app/region/Korea2/"); // don't count invalid coords + + testRegex("Ahern (50,50,50) [1]", url, + "secondlife:///app/region/Ahern/50/50/50/", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [2]", url, + "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [3]", url, + "XXX secondlife:///app/region/Ahern/50/50/50 XXX", + "secondlife:///app/region/Ahern/50/50/50"); + + testRegex("Ahern (50,50,50) multicase", url, + "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50) [1]", url, + "XXX secondlife:///app/region/Ahern/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/"); + + testRegex("Ahern (50,50) [2]", url, + "XXX secondlife:///app/region/Ahern/50/50 XXX", + "secondlife:///app/region/Ahern/50/50"); + + // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat + testRegex("Region with brackets", url, + "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX", + "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30"); + + // Rendering tests. + testLabel("Render /app/region/Ahern/50/50/50/", url, + "secondlife:///app/region/Ahern/50/50/50/", + "Ahern (50,50,50)"); + + testLabel("Render /app/region/Ahern/50/50/50", url, + "secondlife:///app/region/Ahern/50/50/50", + "Ahern (50,50,50)"); + + testLabel("Render /app/region/Ahern/50/50/", url, + "secondlife:///app/region/Ahern/50/50/", + "Ahern (50,50)"); + + testLabel("Render /app/region/Ahern/50/50", url, + "secondlife:///app/region/Ahern/50/50", + "Ahern (50,50)"); + + testLabel("Render /app/region/Ahern/50/", url, + "secondlife:///app/region/Ahern/50/", + "Ahern (50)"); + + testLabel("Render /app/region/Ahern/50", url, + "secondlife:///app/region/Ahern/50", + "Ahern (50)"); + + testLabel("Render /app/region/Ahern/", url, + "secondlife:///app/region/Ahern/", + "Ahern"); + + testLabel("Render /app/region/Ahern/ within context", url, + "XXX secondlife:///app/region/Ahern/ XXX", + "Ahern"); + + testLabel("Render /app/region/Ahern", url, + "secondlife:///app/region/Ahern", + "Ahern"); + + testLabel("Render /app/region/Ahern within context", url, + "XXX secondlife:///app/region/Ahern XXX", + "Ahern"); + + testLabel("Render /app/region/Product%20Engine/", url, + "secondlife:///app/region/Product%20Engine/", + "Product Engine"); + + testLabel("Render /app/region/Product%20Engine", url, + "secondlife:///app/region/Product%20Engine", + "Product Engine"); + + // Location parsing texts. + testLocation("Location /app/region/Ahern/50/50/50/", url, + "secondlife:///app/region/Ahern/50/50/50/", + "Ahern"); + + testLocation("Location /app/region/Product%20Engine", url, + "secondlife:///app/region/Product%20Engine", + "Product Engine"); + } + + template<> template<> + void object::test<13>() + { + // + // test LLUrlEntryemail - general emails + // + LLUrlEntryEmail url; + + // Regex tests. + testRegex("match e-mail addresses", url, + "test@lindenlab.com", + "mailto:test@lindenlab.com"); + + testRegex("match e-mail addresses with mailto: prefix", url, + "mailto:test@lindenlab.com", + "mailto:test@lindenlab.com"); + + testRegex("match e-mail addresses with different domains", url, + "test@foo.org.us", + "mailto:test@foo.org.us"); + + testRegex("match e-mail addresses with different domains", url, + "test@foo.bar", + "mailto:test@foo.bar"); + + testRegex("don't match incorrect e-mail addresses", url, + "test @foo.com", + ""); + + testRegex("don't match incorrect e-mail addresses", url, + "test@ foo.com", + ""); + } + + template<> template<> + void object::test<14>() + { + // + // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com/* and http://*lindenlab.com/* urls + // + LLUrlEntrySecondlifeURL url; + + testRegex("match urls with protocol", url, + "this url should match http://lindenlab.com/products/second-life", + "http://lindenlab.com/products/second-life"); + + testRegex("match urls with protocol", url, + "search something https://marketplace.secondlife.com/products/search on marketplace and test the https", + "https://marketplace.secondlife.com/products/search"); + + testRegex("match HTTPS urls with port", url, + "let's specify some port https://secondlife.com:888/status", + "https://secondlife.com:888/status"); + + testRegex("don't match HTTP urls with port", url, + "let's specify some port for HTTP http://secondlife.com:888/status", + ""); + + testRegex("don't match urls w/o protocol", url, + "looks like an url something www.marketplace.secondlife.com/products but no https prefix", + ""); + + testRegex("but with a protocol www is fine", url, + "so let's add a protocol https://www.marketplace.secondlife.com:8888/products", + "https://www.marketplace.secondlife.com:8888/products"); + + testRegex("don't match urls w/o protocol", url, + "and even no www something secondlife.com/status", + ""); + } + + template<> template<> + void object::test<15>() + { + // + // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com and http://*lindenlab.com urls + // + + LLUrlEntrySimpleSecondlifeURL url; + + testRegex("match urls with a protocol", url, + "this url should match http://lindenlab.com", + "http://lindenlab.com"); + + testRegex("match urls with a protocol", url, + "search something https://marketplace.secondlife.com on marketplace and test the https", + "https://marketplace.secondlife.com"); + + testRegex("don't match urls w/o protocol", url, + "looks like an url something www.marketplace.secondlife.com but no https prefix", + ""); + + testRegex("but with a protocol www is fine", url, + "so let's add a protocol http://www.marketplace.secondlife.com", + "http://www.marketplace.secondlife.com"); + + testRegex("don't match urls w/o protocol", url, + "and even no www something lindenlab.com", + ""); + } + + template<> template<> + void object::test<16>() + { + // + // test LLUrlEntryIPv6 + // + LLUrlEntryIPv6 url; + + // Regex tests. + testRegex("match urls with a protocol", url, + "this url should match http://[::1]", + "http://[::1]"); + + testRegex("match urls with a protocol and query", url, + "this url should match http://[::1]/file.mp3", + "http://[::1]/file.mp3"); + + testRegex("match urls with a protocol", url, + "this url should match http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]", + "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"); + + testRegex("match urls with port", url, + "let's specify some port http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080", + "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080"); + + testRegex("don't match urls w/o protocol", url, + "looks like an url something [2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d] but no https prefix", + ""); + + testRegex("don't match incorrect urls", url, + "http://[ 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d ]", + ""); + } +} -- cgit v1.2.3 From a0b3021bdcf76859054fda8e30abb3ed47749e83 Mon Sep 17 00:00:00 2001 From: Bennett Goble Date: Wed, 29 May 2024 08:10:00 -0700 Subject: Trim trailing whitespace Start trimming trailing whitespace, but limit the blast radius to a handful of file types. --- indra/llui/lltextvalidate.cpp | 10 +++++----- indra/llui/lltextvalidate.h | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp index 9e27ed6232..7ea8a3347e 100644 --- a/indra/llui/lltextvalidate.cpp +++ b/indra/llui/lltextvalidate.cpp @@ -1,25 +1,25 @@ -/** +/** * @file lltextvalidate.cpp * @brief Text validation helper functions * * $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$ */ diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h index 2c2941de7f..a4ff144d82 100644 --- a/indra/llui/lltextvalidate.h +++ b/indra/llui/lltextvalidate.h @@ -1,4 +1,4 @@ -/** +/** * @file lltextbase.h * @author Martin Reddy * @brief The base class of text box/editor, providing Url handling support @@ -6,21 +6,21 @@ * $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$ */ -- cgit v1.2.3 From cb3bd8865aa0f9fb8a247ea595cf1973057ba91f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 30 May 2024 15:41:36 +0200 Subject: Fix a bunch of uninitialized variable warnings that showed up in Visual Studio --- indra/llui/lltoolbar.h | 6 +++--- indra/llui/llui.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lltoolbar.h b/indra/llui/lltoolbar.h index e8ec1e41df..c57c979525 100644 --- a/indra/llui/lltoolbar.h +++ b/indra/llui/lltoolbar.h @@ -178,12 +178,12 @@ public: protected: friend class LLUICtrlFactory; - LLCenterLayoutPanel(const Params& params) : LLLayoutPanel(params), mButtonPanel(NULL) {} + LLCenterLayoutPanel(const Params& params) : LLLayoutPanel(params) {}; private: reshape_callback_t mReshapeCallback; - LLToolBarEnums::EToolBarLocation mLocationId; - LLPanel * mButtonPanel; + LLToolBarEnums::EToolBarLocation mLocationId{ LLToolBarEnums::EToolBarLocation::TOOLBAR_NONE }; + LLPanel * mButtonPanel{ nullptr }; }; struct Params : public LLInitParam::Block diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 471602515d..492c790265 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -234,7 +234,7 @@ public: private: - S32 mValue; + S32 mValue{ 0 }; }; // -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/llui/llaccordionctrl.cpp | 2 +- indra/llui/llclipboard.cpp | 6 +++--- indra/llui/llcombobox.cpp | 2 +- indra/llui/llcommandmanager.cpp | 2 +- indra/llui/llcommandmanager.h | 2 +- indra/llui/llconsole.cpp | 10 +++++----- indra/llui/llemojidictionary.cpp | 2 +- indra/llui/llflatlistview.cpp | 4 ++-- indra/llui/llflatlistview.h | 2 +- indra/llui/llfolderview.cpp | 12 ++++++------ indra/llui/llfolderviewitem.cpp | 12 ++++++------ indra/llui/llkeywords.cpp | 14 +++++++------- indra/llui/llkeywords.h | 4 ++-- indra/llui/lllayoutstack.h | 2 +- indra/llui/lllineeditor.cpp | 16 ++++++++-------- indra/llui/llmenugl.cpp | 16 ++++++++-------- indra/llui/llmodaldialog.h | 2 +- indra/llui/llmultislider.h | 2 +- indra/llui/llnotifications.cpp | 2 +- indra/llui/llnotifications.h | 2 +- indra/llui/llradiogroup.h | 2 +- indra/llui/llresmgr.cpp | 2 +- indra/llui/llscrolllistctrl.cpp | 22 +++++++++++----------- indra/llui/llscrolllistctrl.h | 2 +- indra/llui/llscrolllistitem.cpp | 6 +++--- indra/llui/llspellcheck.cpp | 2 +- indra/llui/llstatbar.cpp | 2 +- indra/llui/lltabcontainer.cpp | 10 +++++----- indra/llui/lltextbase.cpp | 34 +++++++++++++++++----------------- indra/llui/lltextbase.h | 4 ++-- indra/llui/lltexteditor.cpp | 28 ++++++++++++++-------------- indra/llui/lltextparser.cpp | 8 ++++---- indra/llui/lltextvalidate.cpp | 34 +++++++++++++++++----------------- indra/llui/lltimectrl.cpp | 6 +++--- indra/llui/lluistring.h | 2 +- indra/llui/llurlentry.cpp | 16 ++++++++-------- indra/llui/llurlregistry.cpp | 4 ++-- 37 files changed, 150 insertions(+), 150 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 26f5e4fbe2..4682044d6e 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -484,7 +484,7 @@ void LLAccordionCtrl::arrangeMultiple() if (mFitParent) { // All expanded tabs will have equal height - panel_height = calcExpandedTabHeight(i, panel_top); + panel_height = calcExpandedTabHeight(static_cast(i), panel_top); ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height); // Try to make accordion tab fit accordion view height. diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp index 5132d33bbf..64896ccfcb 100644 --- a/indra/llui/llclipboard.cpp +++ b/indra/llui/llclipboard.cpp @@ -73,7 +73,7 @@ bool LLClipboard::addToClipboard(const LLUUID& src, const LLAssetType::EType typ if (LLAssetType::lookupIsAssetIDKnowable(type)) { LLWString source = utf8str_to_wstring(src.asString()); - res = addToClipboard(source, 0, source.size()); + res = addToClipboard(source, 0, static_cast(source.size())); } if (res) { @@ -87,13 +87,13 @@ bool LLClipboard::addToClipboard(const LLUUID& src, const LLAssetType::EType typ bool LLClipboard::pasteFromClipboard(std::vector& inv_objects) const { bool res = false; - S32 count = mObjects.size(); + auto count = mObjects.size(); inv_objects.reserve(inv_objects.size() + count); if (count > 0) { res = true; inv_objects.clear(); - for (S32 i = 0; i < count; i++) + for (size_t i = 0; i < count; i++) { inv_objects.push_back(mObjects[i]); } diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 0d82f29dfb..79dce1c714 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -995,7 +995,7 @@ void LLComboBox::updateSelection() LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel()); LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size()); mTextEntry->setText(wstring_to_utf8str(wtext)); - mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size()); + mTextEntry->setSelection(static_cast(left_wstring.size()), static_cast(mTextEntry->getWText().size())); mTextEntry->endSelection(); mTextEntry->setTentative(false); mHasAutocompletedText = true; diff --git a/indra/llui/llcommandmanager.cpp b/indra/llui/llcommandmanager.cpp index 270ec86e01..03717da80b 100644 --- a/indra/llui/llcommandmanager.cpp +++ b/indra/llui/llcommandmanager.cpp @@ -107,7 +107,7 @@ LLCommandManager::~LLCommandManager() U32 LLCommandManager::commandCount() const { - return mCommands.size(); + return static_cast(mCommands.size()); } LLCommand * LLCommandManager::getCommand(U32 commandIndex) diff --git a/indra/llui/llcommandmanager.h b/indra/llui/llcommandmanager.h index 3b2586a5a1..e6df0d3a4b 100644 --- a/indra/llui/llcommandmanager.h +++ b/indra/llui/llcommandmanager.h @@ -198,7 +198,7 @@ protected: void addCommand(LLCommand * command); private: - typedef std::map CommandIndexMap; + typedef std::map CommandIndexMap; typedef std::vector CommandVector; CommandVector mCommands; diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index be100a6fd2..54bb8cbb94 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -147,11 +147,11 @@ void LLConsole::draw() return; } - U32 num_lines=0; + size_t num_lines{ 0 }; paragraph_t::reverse_iterator paragraph_it; paragraph_it = mParagraphs.rbegin(); - U32 paragraph_num=mParagraphs.size(); + auto paragraph_num=mParagraphs.size(); while (!mParagraphs.empty() && paragraph_it != mParagraphs.rend()) { @@ -159,7 +159,7 @@ void LLConsole::draw() if(num_lines > mMaxLines || ( (mLinePersistTime > (F32)0.f) && ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime) <= (F32)0.f)) { //All lines above here are done. Lose them. - for (U32 i=0;i(color_str.length()); mParagraphColorSegments.push_back(color_segment); } @@ -310,7 +310,7 @@ void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, b skip_chars = 0; } - U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); + U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, static_cast(line_end) - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); if (drawable != 0) { diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp index f16c38a11a..925608e47e 100644 --- a/indra/llui/llemojidictionary.cpp +++ b/indra/llui/llemojidictionary.cpp @@ -210,7 +210,7 @@ void LLEmojiDictionary::findByShortCode( std::size_t begin, end; if (searchInShortCode(begin, end, shortCode, needle)) { - results[begin].emplace_back(d.Character, shortCode, begin, end); + results[static_cast(begin)].emplace_back(d.Character, shortCode, begin, end); } } } diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index b1a95715f1..1799968afb 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -390,7 +390,7 @@ U32 LLFlatListView::size(const bool only_visible_items) const } else { - return mItemPairs.size(); + return static_cast(mItemPairs.size()); } } @@ -563,7 +563,7 @@ void LLFlatListView::rearrangeItems() } // add paddings between items, excluding invisible ones - height += mItemPad * (mItemPairs.size() - invisible_children_count - 1); + height += mItemPad * (static_cast(mItemPairs.size()) - invisible_children_count - 1); LLRect rc = mItemsPanel->getRect(); S32 width = mItemsNoScrollWidth; diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index b64a9862a2..6d75e9f282 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -264,7 +264,7 @@ public: void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; } /** Get number of selected items in the list */ - U32 numSelected() const {return mSelectedItemPairs.size(); } + U32 numSelected() const {return static_cast(mSelectedItemPairs.size()); } /** Get number of (visible) items in the list */ U32 size(const bool only_visible_items = true) const; diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 47821e38ca..1d4ecbe9c9 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -758,7 +758,7 @@ void LLFolderView::removeSelectedItems() // items, since the removal will futz with internal data // structures. std::vector items; - S32 count = mSelectedItems.size(); + auto count = mSelectedItems.size(); if(count <= 0) return; LLFolderViewItem* item = NULL; selected_items_t::iterator item_it; @@ -803,7 +803,7 @@ void LLFolderView::removeSelectedItems() setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus()); listeners.reserve(count); - for(S32 i = 0; i < count; ++i) + for(size_t i = 0; i < count; ++i) { listener = items[i]->getViewModelItem(); if(listener && (std::find(listeners.begin(), listeners.end(), listener) == listeners.end())) @@ -922,7 +922,7 @@ void LLFolderView::copy() { // *NOTE: total hack to clear the inventory clipboard LLClipboard::instance().reset(); - S32 count = mSelectedItems.size(); + auto count = mSelectedItems.size(); if(getVisible() && getEnabled() && (count > 0)) { LLFolderViewModelItem* listener = NULL; @@ -1059,7 +1059,7 @@ void LLFolderView::startRenamingSelectedItem( void ) // make sure selection is visible scrollToShowSelection(); - S32 count = mSelectedItems.size(); + auto count = mSelectedItems.size(); LLFolderViewItem* item = NULL; if(count > 0) { @@ -1414,7 +1414,7 @@ bool LLFolderView::search(LLFolderViewItem* first_item, const std::string &searc std::string current_item_label(search_item->getViewModelItem()->getSearchableName()); LLStringUtil::toUpper(current_item_label); - S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); + auto search_string_length = llmin(upper_case_string.size(), current_item_label.size()); if (!current_item_label.compare(0, search_string_length, upper_case_string)) { found = true; @@ -1454,7 +1454,7 @@ bool LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) mParentPanel.get()->setFocus(true); bool handled = childrenHandleRightMouseDown(x, y, mask) != NULL; - S32 count = mSelectedItems.size(); + auto count = mSelectedItems.size(); LLMenuGL* menu = static_cast(mPopupMenuHandle.get()); if (!menu) diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index a0c7407b06..82cd2483e8 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -940,22 +940,22 @@ void LLFolderViewItem::draw() return; } - std::string::size_type filter_string_length = mViewModelItem->hasFilterStringMatch() ? mViewModelItem->getFilterStringSize() : 0; + auto filter_string_length = mViewModelItem->hasFilterStringMatch() ? static_cast(mViewModelItem->getFilterStringSize()) : 0; F32 right_x = 0; F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; F32 text_left = (F32)getLabelXPos(); std::string combined_string = mLabel + mLabelSuffix; const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL); - S32 filter_offset = mViewModelItem->getFilterStringOffset(); + S32 filter_offset = static_cast(mViewModelItem->getFilterStringOffset()); if (filter_string_length > 0) { S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); S32 top = getRect().getHeight() - TOP_PAD; if(mLabelSuffix.empty() || (font == suffix_font)) { - S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2; - S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2; + S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, static_cast(mViewModelItem->getFilterStringOffset())) - 2; + S32 right = left + font->getWidth(combined_string, static_cast(mViewModelItem->getFilterStringOffset()), filter_string_length) + 2; LLUIImage* box_image = default_params.selection_image; LLRect box_rect(left, top, right, bottom); @@ -976,7 +976,7 @@ void LLFolderViewItem::draw() if(suffix_filter_length > 0) { S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); - S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2; + S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, static_cast(mLabel.size())) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2; S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2; LLUIImage* box_image = default_params.selection_image; LLRect box_rect(left, top, right, bottom); @@ -1033,7 +1033,7 @@ void LLFolderViewItem::draw() if(suffix_filter_length > 0) { S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); - F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length); + F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, static_cast(mLabel.size())) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length); F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index ce644b094c..df446a5d70 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -39,7 +39,7 @@ inline bool LLKeywordToken::isHead(const llwchar* s) const // strncmp is much faster than string compare bool res = true; const llwchar* t = mToken.c_str(); - S32 len = mToken.size(); + auto len = mToken.size(); for (S32 i=0; i* seg_list, const LLW return; } - S32 text_len = wtext.size() + 1; + S32 text_len = static_cast(wtext.size()) + 1; seg_list->push_back( new LLNormalTextSegment( style, 0, text_len, editor ) ); @@ -707,16 +707,16 @@ void LLKeywords::insertSegments(const LLWString& wtext, std::vector(pos), editor); text_segment->setToken( cur_token ); insertSegment( seg_list, text_segment, text_len, style, editor); } - LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, pos); + LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, static_cast(pos)); text_segment->setToken( cur_token ); insertSegment( seg_list, text_segment, text_len, style, editor); - seg_start = pos+1; + seg_start = static_cast(pos) + 1; pos = wtext.find('\n',seg_start); } diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h index 9dcdea121b..f498b3ddee 100644 --- a/indra/llui/llkeywords.h +++ b/indra/llui/llkeywords.h @@ -82,8 +82,8 @@ public: { } - S32 getLengthHead() const { return mToken.size(); } - S32 getLengthTail() const { return mDelimiter.size(); } + S32 getLengthHead() const { return static_cast(mToken.size()); } + S32 getLengthTail() const { return static_cast(mDelimiter.size()); } bool isHead(const llwchar* s) const; bool isTail(const llwchar* s) const; const LLWString& getToken() const { return mToken; } diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index d362d4362c..9e5f8048ba 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -86,7 +86,7 @@ public: void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE); void collapsePanel(LLPanel* panel, bool collapsed = true); - S32 getNumPanels() { return mPanels.size(); } + S32 getNumPanels() { return static_cast(mPanels.size()); } void updateLayout(); diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 5ec4c34f57..50bf3f5877 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -590,7 +590,7 @@ const std::string& LLLineEditor::getSuggestion(U32 index) const U32 LLLineEditor::getSuggestionCount() const { - return mSuggestionList.size(); + return static_cast(mSuggestionList.size()); } void LLLineEditor::replaceWithSuggestion(U32 index) @@ -994,7 +994,7 @@ void LLLineEditor::addChar(const llwchar uni_char) mText.erase(getCursor(), 1); } - S32 cur_bytes = mText.getString().size(); + S32 cur_bytes = static_cast(mText.getString().size()); S32 new_bytes = wchar_utf8_length(new_c); @@ -1007,7 +1007,7 @@ void LLLineEditor::addChar(const llwchar uni_char) } else if (mMaxLengthChars) { - S32 wide_chars = mText.getWString().size(); + auto wide_chars = mText.getWString().size(); if ((wide_chars + 1) > mMaxLengthChars) { allow_char = false; @@ -1332,7 +1332,7 @@ void LLLineEditor::pasteHelper(bool is_primary) if (mMaxLengthChars) { - U32 available_chars = mMaxLengthChars - mText.getWString().size(); + auto available_chars = mMaxLengthChars - mText.getWString().size(); if (available_chars < clean_string.size()) { @@ -2300,10 +2300,10 @@ bool LLLineEditor::postvalidateFloat(const std::string &str) LLWString trimmed = utf8str_to_wstring(str); LLWStringUtil::trim(trimmed); - S32 len = trimmed.length(); + auto len = trimmed.length(); if( 0 < len ) { - S32 i = 0; + size_t i = 0; // First character can be a negative sign if( '-' == trimmed[0] ) @@ -2360,7 +2360,7 @@ bool LLLineEditor::evaluateFloat() if (!success) { // Move the cursor to near the error on failure - setCursor(LLCalc::getInstance()->getLastErrorPos()); + setCursor(static_cast(LLCalc::getInstance()->getLastErrorPos())); // *TODO: Translated error message indicating the type of error? Select error text? } else @@ -2501,7 +2501,7 @@ void LLLineEditor::updatePreedit(const LLWString &preedit_string, if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) ); - mText.erase(insert_preedit_at, mPreeditWString.length()); + mText.erase(insert_preedit_at, static_cast(mPreeditWString.length())); } else { diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 279f5628e1..dffaf69a9f 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -187,7 +187,7 @@ LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) { mAcceleratorMask |= MASK_SHIFT; } - S32 pipe_pos = shortcut.rfind("|"); + auto pipe_pos = shortcut.rfind("|"); std::string key_str = shortcut.substr(pipe_pos+1); LLKeyboard::keyFromString(key_str, &mAcceleratorKey); @@ -545,8 +545,8 @@ void LLMenuItemGL::draw( void ) std::string::size_type offset = upper_case_label.find(mJumpKey); if (offset != std::string::npos) { - S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset); - S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1); + S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, static_cast(offset)); + S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, static_cast(offset) + 1); gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1); } } @@ -1649,8 +1649,8 @@ void LLMenuItemBranchDownGL::draw( void ) if (offset != std::string::npos) { S32 x_offset = ll_round((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f); - S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset); - S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1); + S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, static_cast(offset)); + S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, static_cast(offset) + 1); gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS); } } @@ -2068,7 +2068,7 @@ bool LLMenuGL::scrollItems(EScrollingDirection direction) // Otherwise viewer will hang for a time needed to scroll U32_MAX // times in std::advance(). STORM-659. size_t nitems = mItems.size(); - U32 scrollable_items = nitems < mMaxScrollableItems ? nitems : mMaxScrollableItems; + U32 scrollable_items = nitems < mMaxScrollableItems ? static_cast(nitems) : mMaxScrollableItems; // Advance by mMaxScrollableItems back from the end of the list // to make the last item visible. @@ -2594,7 +2594,7 @@ void LLMenuGL::empty( void ) // erase group of items from menu void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/) { - S32 items = mItems.size(); + auto items = mItems.size(); if ( items == 0 || begin >= end || begin < 0 || end > items ) { @@ -2813,7 +2813,7 @@ void LLMenuGL::setTornOff(bool torn_off) U32 LLMenuGL::getItemCount() { - return mItems.size(); + return static_cast(mItems.size()); } LLMenuItemGL* LLMenuGL::getItem(S32 number) diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h index 58c253c3f4..177664dde4 100644 --- a/indra/llui/llmodaldialog.h +++ b/indra/llui/llmodaldialog.h @@ -66,7 +66,7 @@ public: static void onAppFocusLost(); static void onAppFocusGained(); - static S32 activeCount() { return sModalStack.size(); } + static S32 activeCount() { return static_cast(sModalStack.size()); } static void shutdownModals(); protected: diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h index 630e45dddb..b2bfc8bc84 100644 --- a/indra/llui/llmultislider.h +++ b/indra/llui/llmultislider.h @@ -118,7 +118,7 @@ public: /*virtual*/ void draw() override; S32 getMaxNumSliders() { return mMaxNumSliders; } - S32 getCurNumSliders() { return mValue.size(); } + S32 getCurNumSliders() { return static_cast(mValue.size()); } F32 getOverlapThreshold() { return mOverlapThreshold; } bool canAddSliders() { return mValue.size() < mMaxNumSliders; } diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 74e573bb4e..bee7d5bb3f 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1187,7 +1187,7 @@ bool LLNotificationChannel::isEmpty() const S32 LLNotificationChannel::size() const { - return mItems.size(); + return static_cast(mItems.size()); } size_t LLNotificationChannel::size() diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index e7159de94c..d3615b6601 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -249,7 +249,7 @@ public: void fromLLSD(const LLSD& sd); LLSD asLLSD() const; - S32 getNumElements() { return mFormData.size(); } + S32 getNumElements() { return static_cast(mFormData.size()); } LLSD getElement(S32 index) { return mFormData.get(index); } LLSD getElement(const std::string& element_name); void getElements(LLSD& elements, S32 offset = 0); diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index 810830ffa4..a24a7c86b2 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -87,7 +87,7 @@ public: LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }; // LLCtrlSelectionInterface functions - /*virtual*/ S32 getItemCount() const { return mRadioButtons.size(); } + /*virtual*/ S32 getItemCount() const { return static_cast(mRadioButtons.size()); } /*virtual*/ bool getCanSelect() const { return true; } /*virtual*/ bool selectFirstItem() { return setSelectedIndex(0); } /*virtual*/ bool selectNthItem( S32 index ) { return setSelectedIndex(index); } diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp index d07aa800d1..21bdf3f8a6 100644 --- a/indra/llui/llresmgr.cpp +++ b/indra/llui/llresmgr.cpp @@ -140,7 +140,7 @@ std::string LLResMgr::getMonetaryString( S32 input ) const S32 output_pos = 0; cur_group = 0; - S32 pos = digits.size()-1; + auto pos = digits.size()-1; S32 count_within_group = 0; while( (pos >= 0) && (groupings[cur_group] >= 0) ) { diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 39e575173d..74a9641836 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -373,7 +373,7 @@ S32 LLScrollListCtrl::isEmpty() const S32 LLScrollListCtrl::getItemCount() const { - return mItemList.size(); + return static_cast(mItemList.size()); } bool LLScrollListCtrl::hasSelectedItem() const @@ -1325,7 +1325,7 @@ bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target, bool case_sen bool found = false; LLWString target_trimmed( target ); - S32 target_len = target_trimmed.size(); + auto target_len = target_trimmed.size(); if( 0 == target_len ) { @@ -1377,8 +1377,8 @@ bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target, bool case_sen if (select) { // find offset of matching text (might have leading whitespace) - S32 offset = item_label.find(target_trimmed); - cellp->highlightText(offset, target_trimmed.size()); + auto offset = item_label.find(target_trimmed); + cellp->highlightText(static_cast(offset), static_cast(target_trimmed.size())); selectItem(item, -1); found = true; break; @@ -1404,7 +1404,7 @@ U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitiv U32 found = 0; LLWString substring_trimmed(substring); - S32 len = substring_trimmed.size(); + auto len = substring_trimmed.size(); if (0 == len) { @@ -1446,7 +1446,7 @@ U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitiv if (found_iter != std::string::npos) { // find offset of matching text - cellp->highlightText(found_iter, substring_trimmed.size()); + cellp->highlightText(static_cast(found_iter), static_cast(substring_trimmed.size())); selectItem(item, -1, false); found++; @@ -2897,7 +2897,7 @@ void LLScrollListCtrl::copy() { buffer += (*itor)->getContentsCSV() + "\n"; } - LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length()); + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, static_cast(buffer.length())); } // virtual @@ -2981,7 +2981,7 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params // Add column mColumns[name] = new LLScrollListColumn(column_params, this); LLScrollListColumn* new_column = mColumns[name]; - new_column->mIndex = mColumns.size()-1; + new_column->mIndex = static_cast(mColumns.size()) - 1; // Add button if (new_column->getWidth() > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth) @@ -3177,7 +3177,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; if (!item_p.validateBlock() || !new_item) return NULL; - new_item->setNumColumns(mColumns.size()); + new_item->setNumColumns(static_cast(mColumns.size())); // Add any columns we don't already have S32 col_index = 0; @@ -3212,7 +3212,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS } addColumn(new_column); columnp = mColumns[column]; - new_item->setNumColumns(mColumns.size()); + new_item->setNumColumns(static_cast(mColumns.size())); } S32 index = columnp->mIndex; @@ -3245,7 +3245,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS new_column.name = "0"; addColumn(new_column); - new_item->setNumColumns(mColumns.size()); + new_item->setNumColumns(static_cast(mColumns.size())); } LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value)); diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 356d40ce3c..f25ba61fd4 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -178,7 +178,7 @@ public: virtual bool preProcessChildNode(LLXMLNodePtr child); virtual LLScrollListColumn* getColumn(S32 index); virtual LLScrollListColumn* getColumn(const std::string& name); - virtual S32 getNumColumns() const { return mColumnsIndexed.size(); } + virtual S32 getNumColumns() const { return static_cast(mColumnsIndexed.size()); } // Adds a single element, from an array of: // "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp index 85da55e3e6..0d79d19a37 100644 --- a/indra/llui/llscrolllistitem.cpp +++ b/indra/llui/llscrolllistitem.cpp @@ -85,7 +85,7 @@ void LLScrollListItem::addColumn(const LLScrollListCell::Params& p) void LLScrollListItem::setNumColumns(S32 columns) { - S32 prev_columns = mColumns.size(); + auto prev_columns = mColumns.size(); if (columns < prev_columns) { std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer()); @@ -93,7 +93,7 @@ void LLScrollListItem::setNumColumns(S32 columns) mColumns.resize(columns); - for (S32 col = prev_columns; col < columns; ++col) + for (auto col = prev_columns; col < columns; ++col) { mColumns[col] = NULL; } @@ -115,7 +115,7 @@ void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell ) S32 LLScrollListItem::getNumColumns() const { - return mColumns.size(); + return static_cast(mColumns.size()); } LLScrollListCell* LLScrollListItem::getColumn(const S32 i) const diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp index b8aeb3b91f..1615db5b52 100644 --- a/indra/llui/llspellcheck.cpp +++ b/indra/llui/llspellcheck.cpp @@ -94,7 +94,7 @@ S32 LLSpellChecker::getSuggestions(const std::string& word, std::vectorfree_list(&suggestion_list, suggestion_cnt); } - return suggestions.size(); + return static_cast(suggestions.size()); } const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_language) diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp index f125dda916..adb1d51813 100644 --- a/indra/llui/llstatbar.cpp +++ b/indra/llui/llstatbar.cpp @@ -455,7 +455,7 @@ void LLStatBar::draw() if (mDisplayHistory && mStat.valid) { - const S32 num_values = frame_recording.getNumRecordedPeriods() - 1; + const S32 num_values = static_cast(frame_recording.getNumRecordedPeriods()) - 1; F32 min_value = 0.f, max_value = 0.f; diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 752ef47d14..06f584d372 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -1298,7 +1298,7 @@ void LLTabContainer::removeTabPanel(LLPanel* child) if (mCurrentTabIdx >= (S32)mTabList.size()) { - mCurrentTabIdx = mTabList.size()-1; + mCurrentTabIdx = static_cast(mTabList.size()) - 1; } selectTab(mCurrentTabIdx); if (has_focus) @@ -1377,7 +1377,7 @@ S32 LLTabContainer::getCurrentPanelIndex() S32 LLTabContainer::getTabCount() { - return mTabList.size(); + return static_cast(mTabList.size()); } LLPanel* LLTabContainer::getPanelByIndex(S32 index) @@ -1444,7 +1444,7 @@ void LLTabContainer::selectFirstTab() void LLTabContainer::selectLastTab() { - selectTab( mTabList.size()-1 ); + selectTab(static_cast(mTabList.size()) - 1); } void LLTabContainer::selectNextTab() @@ -1482,12 +1482,12 @@ void LLTabContainer::selectPrevTab() } S32 idx = mCurrentTabIdx-1; if (idx < 0) - idx = mTabList.size()-1; + idx = static_cast(mTabList.size()) - 1; while (!selectTab(idx) && idx != mCurrentTabIdx) { idx = idx - 1; if (idx < 0) - idx = mTabList.size()-1; + idx = static_cast(mTabList.size()) - 1; } if (tab_has_focus) { diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 1249461be9..387f2b9810 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -320,12 +320,12 @@ bool LLTextBase::truncate() if (value.type() == LLSD::TypeString) { // save a copy for strings. - utf8_byte_size = value.size(); + utf8_byte_size = static_cast(value.size()); } else { // non string LLSDs need explicit conversion to string - utf8_byte_size = value.asString().size(); + utf8_byte_size = static_cast(value.asString().size()); } if ( utf8_byte_size > mMaxTextByteLength ) @@ -335,7 +335,7 @@ bool LLTextBase::truncate() temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); LLWString text = utf8str_to_wstring( temp_utf8_text ); // remove extra bit of current string, to preserve formatting, etc. - removeStringNoUndo(text.size(), getWText().size() - text.size()); + removeStringNoUndo(static_cast(text.size()), static_cast(getWText().size() - text.size())); did_truncate = true; } } @@ -617,7 +617,7 @@ void LLTextBase::drawText() } else if (useLabel()) { - text_len = mLabel.getWString().length(); + text_len = static_cast(mLabel.getWString().length()); } S32 selection_left = -1; @@ -686,7 +686,7 @@ void LLTextBase::drawText() // Find the start of the first word U32 word_start = seg_start, word_end = -1; - U32 text_length = wstrText.length(); + U32 text_length = static_cast(wstrText.length()); while ( (word_start < text_length) && (!LLStringOps::isAlpha(wstrText[word_start])) ) { word_start++; @@ -845,7 +845,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s beforeValueChange(); S32 old_len = getLength(); // length() returns character length - S32 insert_len = wstr.length(); + S32 insert_len = static_cast(wstr.length()); pos = getEditableIndex(pos, true); if (pos > old_len) @@ -916,7 +916,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s { LLStyleSP emoji_style; LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL; - for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++) + for (S32 text_kitty = 0, text_len = static_cast(wstr.size()); text_kitty < text_len; text_kitty++) { llwchar code = wstr[text_kitty]; bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code); @@ -1469,7 +1469,7 @@ const std::string& LLTextBase::getSuggestion(U32 index) const U32 LLTextBase::getSuggestionCount() const { - return mSuggestionList.size(); + return static_cast(mSuggestionList.size()); } void LLTextBase::replaceWithSuggestion(U32 index) @@ -2335,7 +2335,7 @@ void LLTextBase::resetLabel() style->setColor(mTentativeFgColor); LLStyleConstSP sp(style); - LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, mLabel.getWString().length() + 1, *this); + LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, static_cast(mLabel.getWString().length()) + 1, *this); insertSegment(label); } } @@ -2395,7 +2395,7 @@ void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const s { segment_vec_t segments; LLWString widget_wide_text = utf8str_to_wstring(text); - segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + widget_wide_text.size())); + segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + static_cast(widget_wide_text.size()))); insertStringNoUndo(getLength(), widget_wide_text, &segments); } @@ -2436,11 +2436,11 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig { highlight_params.font.style("NORMAL"); LLStyleConstSP normal_sp(new LLStyle(highlight_params)); - segmentp = new LLOnHoverChangeableTextSegment(sp, normal_sp, cur_length, cur_length + wide_text.size(), *this); + segmentp = new LLOnHoverChangeableTextSegment(sp, normal_sp, cur_length, cur_length + static_cast(wide_text.size()), *this); } else { - segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this); + segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + static_cast(wide_text.size()), *this); } segment_vec_t segments; segments.push_back(segmentp); @@ -2454,7 +2454,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig segment_vec_t segments; S32 segment_start = old_length; - S32 segment_end = old_length + wide_text.size(); + S32 segment_end = old_length + static_cast(wide_text.size()); LLStyleConstSP sp(new LLStyle(style_params)); if (underline_on_hover_only || mSkipLinkUnderline) { @@ -2545,7 +2545,7 @@ void LLTextBase::replaceUrl(const std::string &url, S32 start = seg->getStart(); S32 end = seg->getEnd(); text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1); - seg->setEnd(start + wlabel.size()); + seg->setEnd(start + static_cast(wlabel.size())); modified = true; } @@ -2898,7 +2898,7 @@ bool LLTextBase::setCursor(S32 row, S32 column) { if (row < 0 || column < 0) return false; - S32 n_lines = mLineInfoList.size(); + S32 n_lines = static_cast(mLineInfoList.size()); for (S32 line = row; line < n_lines; ++line) { const line_info& li = mLineInfoList[line]; @@ -3537,7 +3537,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin : LLFontGL::ONLY_WORD_BOUNDARIES; - S32 offsetLength = text.length() - (segment_offset + mStart); + S32 offsetLength = static_cast(text.length()) - (segment_offset + mStart); if(getLength() < segment_offset + mStart) { @@ -3617,7 +3617,7 @@ const LLWString& LLLabelTextSegment::getWText() const /*virtual*/ const S32 LLLabelTextSegment::getLength() const { - return mEditor.getWlabel().length(); + return static_cast(mEditor.getWlabel().length()); } // diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index f6c7ce6e81..4120d9ea32 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -454,8 +454,8 @@ public: // force reflow of text void needsReflow(S32 index = 0); - S32 getLength() const { return getWText().length(); } - S32 getLineCount() const { return mLineInfoList.size(); } + S32 getLength() const { return static_cast(getWText().length()); } + S32 getLineCount() const { return static_cast(mLineInfoList.size()); } S32 removeFirstLine(); // returns removed length void addDocumentChild(LLView* view); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index b4254524ad..dc3026e14d 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -98,13 +98,13 @@ public: } virtual S32 undo( LLTextBase* editor ) { - remove(editor, getPosition(), mWString.length() ); + remove(editor, getPosition(), static_cast(mWString.length())); return getPosition(); } virtual S32 redo( LLTextBase* editor ) { - insert(editor, getPosition(), mWString ); - return getPosition() + mWString.length(); + insert(editor, getPosition(), mWString); + return getPosition() + static_cast(mWString.length()); } private: @@ -151,13 +151,13 @@ public: } virtual S32 undo( LLTextBase* editor ) { - remove(editor, getPosition(), mWString.length() ); + remove(editor, getPosition(), static_cast(mWString.length())); return getPosition(); } virtual S32 redo( LLTextBase* editor ) { - insert(editor, getPosition(), mWString ); - return getPosition() + mWString.length(); + insert(editor, getPosition(), mWString); + return getPosition() + static_cast(mWString.length()); } private: @@ -216,7 +216,7 @@ public: virtual S32 undo( LLTextBase* editor ) { insert(editor, getPosition(), mWString); - return getPosition() + mWString.length(); + return getPosition() + static_cast(mWString.length()); } virtual S32 redo( LLTextBase* editor ) { @@ -365,16 +365,16 @@ void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insen if (selected_text == search_text) { // We already have this word selected, we are searching for the next. - setCursorPos(mCursorPos + search_text.size()); + setCursorPos(mCursorPos + static_cast(search_text.size())); } } - S32 loc = text.find(search_text,mCursorPos); + S32 loc = static_cast(text.find(search_text,mCursorPos)); // If Maybe we wrapped, search again if (wrap && (-1 == loc)) { - loc = text.find(search_text); + loc = static_cast(text.find(search_text)); } // If still -1, then search_text just isn't found. @@ -1578,8 +1578,8 @@ void LLTextEditor::cleanStringForPaste(LLWString & clean_string) if( mAllowEmbeddedItems ) { const llwchar LF = 10; - S32 len = clean_string.length(); - for( S32 i = 0; i < len; i++ ) + auto len = clean_string.length(); + for( size_t i = 0; i < len; i++ ) { llwchar wc = clean_string[i]; if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) ) @@ -2489,7 +2489,7 @@ void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const LLWString widget_wide_text = utf8str_to_wstring(text); - LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size()); + LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + static_cast(widget_wide_text.size())); insert(getLength(), widget_wide_text, false, segment); // Set the cursor and scroll position @@ -2843,7 +2843,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length()); - removeStringNoUndo(insert_preedit_at, mPreeditWString.length()); + removeStringNoUndo(insert_preedit_at, static_cast(mPreeditWString.length())); } else { diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp index 8e2bfe6ac2..6a867fc759 100644 --- a/indra/llui/lltextparser.cpp +++ b/indra/llui/lltextparser.cpp @@ -73,11 +73,11 @@ S32 LLTextParser::findPattern(const std::string &text, LLSD highlight) found = (! ltext.find(pattern) ? 0 : std::string::npos); break; case ENDS_WITH: - S32 pos = ltext.rfind(pattern); + auto pos = ltext.rfind(pattern); if (pos >= 0 && (ltext.length()-pattern.length()==pos)) found = pos; break; } - return found; + return static_cast(found); } LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index) @@ -99,8 +99,8 @@ LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLC S32 start = findPattern(text,mHighlights[i]); if (start >= 0 ) { - S32 end = std::string(mHighlights[i]["pattern"]).length(); - S32 len = text.length(); + auto end = std::string(mHighlights[i]["pattern"]).length(); + auto len = text.length(); EHighlightPosition newpart; if (start==0) { diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp index 9e27ed6232..f9de6f7386 100644 --- a/indra/llui/lltextvalidate.cpp +++ b/indra/llui/lltextvalidate.cpp @@ -73,7 +73,7 @@ class ValidatorFloat : public ValidatorImpl std::basic_string trimmed = str; LLStringUtilBase::trim(trimmed); - S32 len = trimmed.length(); + auto len = trimmed.length(); if (0 < len) { // May be a comma or period, depending on the locale @@ -118,7 +118,7 @@ class ValidatorInt : public ValidatorImpl std::basic_string trimmed = str; LLStringUtilBase::trim(trimmed); - S32 len = trimmed.length(); + auto len = trimmed.length(); if (0 < len) { S32 i = 0; @@ -157,7 +157,7 @@ class ValidatorPositiveS32 : public ValidatorImpl std::basic_string trimmed = str; LLStringUtilBase::trim(trimmed); - S32 len = trimmed.length(); + auto len = trimmed.length(); if (0 < len) { CHAR ch = trimmed.front(); @@ -167,7 +167,7 @@ class ValidatorPositiveS32 : public ValidatorImpl return setError("Validator_ShouldNotBeMinusOrZero", LLSD().with("CH", llsd(ch))); } - for (S32 i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) { ch = trimmed[i]; if (!LLStringOps::isDigit(ch)) @@ -177,7 +177,7 @@ class ValidatorPositiveS32 : public ValidatorImpl } } - S32 val = strtol(trimmed); + auto val = strtol(trimmed); if (val <= 0) { return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(trimmed))); @@ -201,7 +201,7 @@ class ValidatorNonNegativeS32 : public ValidatorImpl std::basic_string trimmed = str; LLStringUtilBase::trim(trimmed); - S32 len = trimmed.length(); + auto len = trimmed.length(); if (0 < len) { CHAR ch = trimmed.front(); @@ -211,7 +211,7 @@ class ValidatorNonNegativeS32 : public ValidatorImpl return setError("Validator_ShouldNotBeMinus", LLSD().with("CH", llsd(ch))); } - for (S32 i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) { ch = trimmed[i]; if (!LLStringOps::isDigit(ch)) @@ -221,7 +221,7 @@ class ValidatorNonNegativeS32 : public ValidatorImpl } } - S32 val = strtol(trimmed); + auto val = strtol(trimmed); if (val < 0) { return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(trimmed))); @@ -244,7 +244,7 @@ class ValidatorNonNegativeS32NoSpace : public ValidatorImpl LLLocale locale(LLLocale::USER_LOCALE); std::basic_string test_str = str; - S32 len = test_str.length(); + auto len = test_str.length(); if (0 < len) { CHAR ch = test_str.front(); @@ -254,7 +254,7 @@ class ValidatorNonNegativeS32NoSpace : public ValidatorImpl return setError("Validator_ShouldNotBeMinus", LLSD().with("CH", llsd(ch))); } - for (S32 i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) { ch = test_str[i]; if (!LLStringOps::isDigit(ch) || LLStringOps::isSpace(ch)) @@ -264,7 +264,7 @@ class ValidatorNonNegativeS32NoSpace : public ValidatorImpl } } - S32 val = strtol(test_str); + auto val = strtol(test_str); if (val < 0) { return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(test_str))); @@ -286,7 +286,7 @@ class ValidatorAlphaNum : public ValidatorImpl { LLLocale locale(LLLocale::USER_LOCALE); - S32 len = str.length(); + auto len = str.length(); while (len--) { CHAR ch = str[len]; @@ -313,7 +313,7 @@ class ValidatorAlphaNumSpace : public ValidatorImpl { LLLocale locale(LLLocale::USER_LOCALE); - S32 len = str.length(); + auto len = str.length(); while (len--) { CHAR ch = str[len]; @@ -341,7 +341,7 @@ class ValidatorASCIIPrintableNoPipe : public ValidatorImpl template bool validate(const std::basic_string& str) { - S32 len = str.length(); + auto len = str.length(); while (len--) { CHAR ch = str[len]; @@ -368,7 +368,7 @@ class ValidatorASCIIPrintableNoSpace : public ValidatorImpl template bool validate(const std::basic_string& str) { - S32 len = str.length(); + auto len = str.length(); while (len--) { CHAR ch = str[len]; @@ -395,7 +395,7 @@ protected: template bool validate(const std::basic_string& str) { - S32 len = str.length(); + auto len = str.length(); while (len--) { CHAR ch = str[len]; @@ -441,7 +441,7 @@ class ValidatorASCIIWithNewLine : public ValidatorImpl template bool validate(const std::basic_string& str) { - S32 len = str.length(); + auto len = str.length(); while (len--) { CHAR ch = str[len]; diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp index f1bf60d262..e2e735b131 100644 --- a/indra/llui/lltimectrl.cpp +++ b/indra/llui/lltimectrl.cpp @@ -343,7 +343,7 @@ LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart() S32 cur_pos = mEditor->getCursor(); std::string time_str = mEditor->getText(); - S32 colon_index = time_str.find_first_of(':'); + auto colon_index = time_str.find_first_of(':'); if (cur_pos <= colon_index) { @@ -376,7 +376,7 @@ std::string LLTimeCtrl::getMinutesString(const std::string& str) size_t colon_index = str.find_first_of(':'); ++colon_index; - int minutes_len = str.length() - colon_index - AMPM_LEN; + auto minutes_len = str.length() - colon_index - AMPM_LEN; std::string minutes_str = str.substr(colon_index, minutes_len); return minutes_str; @@ -411,7 +411,7 @@ bool LLTimeCtrl::isMinutesStringValid(const std::string& str) // static bool LLTimeCtrl::isPMAMStringValid(const std::string& str) { - S32 len = str.length(); + auto len = str.length(); bool valid = (str[--len] == 'M') && (str[--len] == 'P' || str[len] == 'A'); diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h index 0cc699f59c..b9d4ff0ebb 100644 --- a/indra/llui/lluistring.h +++ b/indra/llui/lluistring.h @@ -79,7 +79,7 @@ public: operator LLWString() const { return getUpdatedWResult(); } bool empty() const { return getUpdatedResult().empty(); } - S32 length() const { return getUpdatedWResult().size(); } + S32 length() const { return static_cast(getUpdatedWResult().size()); } void clear(); void clearArgs() { if (mArgs) mArgs->clear(); } diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 234ce9f4c3..198441804b 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -389,7 +389,7 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const LLURI uri(url); LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); + auto path_parts = path_array.size(); S32 x,y,z; if (path_parts == actual_parts) @@ -454,7 +454,7 @@ std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCa LLURI uri(url); LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); + auto path_parts = path_array.size(); if (path_parts == 5) { // handle slurl with (X,Y,Z) coordinates @@ -1074,7 +1074,7 @@ LLUrlEntryParcel::~LLUrlEntryParcel() std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) { LLSD path_array = LLURI(url).pathArray(); - S32 path_parts = path_array.size(); + auto path_parts = path_array.size(); if (path_parts < 3) // no parcel id { @@ -1165,7 +1165,7 @@ std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCa LLURI uri(url); std::string location = unescapeUrl(uri.hostName()); LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); + auto path_parts = path_array.size(); if (path_parts == 3) { // handle slurl with (X,Y,Z) coordinates @@ -1214,7 +1214,7 @@ std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelC // LLSD path_array = LLURI(url).pathArray(); - S32 path_parts = path_array.size(); + auto path_parts = path_array.size(); if (path_parts < 3) // no region name { @@ -1278,7 +1278,7 @@ std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabe // LLURI uri(url); LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); + auto path_parts = path_array.size(); std::string host = uri.hostName(); std::string label = LLTrans::getString("SLurlLabelTeleport"); if (!host.empty()) @@ -1413,7 +1413,7 @@ std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabe // LLURI uri(url); LLSD path_array = uri.pathArray(); - S32 path_parts = path_array.size(); + auto path_parts = path_array.size(); if (path_parts < 3) { return url; @@ -1505,7 +1505,7 @@ LLUrlEntryEmail::LLUrlEntryEmail() std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb) { - int pos = url.find("mailto:"); + auto pos = url.find("mailto:"); if (pos == std::string::npos) { diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 9c3994480c..3a4ce6a72f 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -263,9 +263,9 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr { return false; } - S32 end = start + wurl.size() - 1; + auto end = start + wurl.size() - 1; - match.setValues(start, end, match.getUrl(), + match.setValues(static_cast(start), static_cast(end), match.getUrl(), match.getLabel(), match.getQuery(), match.getTooltip(), -- cgit v1.2.3 From 9e45c1e506e0cdf8ade6136da9bbf867c93d16e6 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sun, 9 Jun 2024 16:17:03 +0200 Subject: Fix issues resulting from b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 --- indra/llui/llresmgr.cpp | 2 +- indra/llui/lltextparser.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp index 21bdf3f8a6..111c732548 100644 --- a/indra/llui/llresmgr.cpp +++ b/indra/llui/llresmgr.cpp @@ -140,7 +140,7 @@ std::string LLResMgr::getMonetaryString( S32 input ) const S32 output_pos = 0; cur_group = 0; - auto pos = digits.size()-1; + S32 pos = static_cast(digits.size()) - 1; S32 count_within_group = 0; while( (pos >= 0) && (groupings[cur_group] >= 0) ) { diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp index 6a867fc759..097b168106 100644 --- a/indra/llui/lltextparser.cpp +++ b/indra/llui/lltextparser.cpp @@ -74,7 +74,7 @@ S32 LLTextParser::findPattern(const std::string &text, LLSD highlight) break; case ENDS_WITH: auto pos = ltext.rfind(pattern); - if (pos >= 0 && (ltext.length()-pattern.length()==pos)) found = pos; + if (pos != std::string::npos && pos >= 0 && (ltext.length() - pattern.length() == pos)) found = pos; break; } return static_cast(found); -- cgit v1.2.3 From c0fad3028fd55c2067ce6a0ae4382cffe1014284 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 10 Jun 2024 16:42:43 +0200 Subject: Re-enable compiler warnings C4018, C4100, C4231 and C4506 --- indra/llui/lltextbase.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 387f2b9810..3a40ea07fb 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -789,7 +789,7 @@ void LLTextBase::drawText() } // Draw squiggly lines under any visible misspelled words - while ( (mMisspellRanges.end() != misspell_it) && (misspell_it->first < seg_end) && (misspell_it->second > seg_start) ) + while ( (mMisspellRanges.end() != misspell_it) && (misspell_it->first < (U32)seg_end) && (misspell_it->second > (U32)seg_start) ) { // Skip the current word if the user is still busy editing it if ( (!mSpellCheckTimer.hasExpired()) && (misspell_it->first <= (U32)mCursorPos) && (misspell_it->second >= (U32)mCursorPos) ) @@ -798,7 +798,7 @@ void LLTextBase::drawText() continue; } - U32 misspell_start = llmax(misspell_it->first, seg_start), misspell_end = llmin(misspell_it->second, seg_end); + U32 misspell_start = llmax(misspell_it->first, (U32)seg_start), misspell_end = llmin(misspell_it->second, (U32)seg_end); S32 squiggle_start = 0, squiggle_end = 0, pony = 0; cur_segment->getDimensions(seg_start - cur_segment->getStart(), misspell_start - seg_start, squiggle_start, pony); cur_segment->getDimensions(misspell_start - cur_segment->getStart(), misspell_end - misspell_start, squiggle_end, pony); @@ -821,7 +821,7 @@ void LLTextBase::drawText() squiggle_start += 4; } - if (misspell_it->second > seg_end) + if (misspell_it->second > (U32)seg_end) { break; } -- cgit v1.2.3 From 4b52dd754b41948efca0087ccac6d813f1fcce3f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 10 Jun 2024 18:16:13 +0200 Subject: Fix incorrect use of VX/VY/VZ/VW indices when color components are accessed --- indra/llui/llkeywords.cpp | 6 +++--- indra/llui/lltextbase.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index df446a5d70..5e184b5ddb 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -803,9 +803,9 @@ void LLKeywords::dump() void LLKeywordToken::dump() { LL_INFOS() << "[" << - mColor.mV[VX] << ", " << - mColor.mV[VY] << ", " << - mColor.mV[VZ] << "] [" << + mColor.mV[VRED] << ", " << + mColor.mV[VGREEN] << ", " << + mColor.mV[VBLUE] << "] [" << wstring_to_utf8str(mToken) << "]" << LL_ENDL; } diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 3a40ea07fb..1d358a0e9d 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -3579,9 +3579,9 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin void LLNormalTextSegment::dump() const { LL_INFOS() << "Segment [" << -// mColor.mV[VX] << ", " << -// mColor.mV[VY] << ", " << -// mColor.mV[VZ] << "]\t[" << +// mColor.mV[VRED] << ", " << +// mColor.mV[VGREEN] << ", " << +// mColor.mV[VBLUE] << "]\t[" << mStart << ", " << getEnd() << "]" << LL_ENDL; -- cgit v1.2.3 From c95b4bf3ea2b681d6d05468b07e60fedb71fa2cf Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 10 Jun 2024 20:42:42 +0300 Subject: Post-merge - trim trailing whitespace --- indra/llui/llviewereventrecorder.h | 22 +++++++++++----------- indra/llui/llxyvector.cpp | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h index e749e1ab57..9e752e8090 100644 --- a/indra/llui/llviewereventrecorder.h +++ b/indra/llui/llviewereventrecorder.h @@ -3,22 +3,22 @@ * @brief Viewer event recording and playback support for mouse and keyboard events * * $LicenseInfo:firstyear=2013&license=viewerlgpl$ - * + * * Copyright (c) 2013, 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$ */ @@ -27,10 +27,10 @@ #define LL_VIEWER_EVENT_RECORDER -#include "linden_common.h" +#include "linden_common.h" -#include "lldir.h" -#include "llsd.h" +#include "lldir.h" +#include "llsd.h" #include "llfile.h" #include "lldate.h" #include "llsdserialize.h" @@ -46,7 +46,7 @@ class LLViewerEventRecorder : public LLSimpleton public: LLViewerEventRecorder(); ~LLViewerEventRecorder(); - + void updateMouseEventInfo(S32 local_x,S32 local_y, S32 global_x, S32 global_y, std::string mName); void setMouseLocalCoords(S32 x,S32 y); void setMouseGlobalCoords(S32 x,S32 y); @@ -75,12 +75,12 @@ public: bool logEvents; std::string mLogFilename; - llofstream mLog; + llofstream mLog; private: - // Mouse event info + // Mouse event info S32 global_x; S32 global_y; S32 local_x; @@ -92,7 +92,7 @@ public: // Actually write the event out to llsd log file void recordEvent(LLSD event); - void clear(S32 r); + void clear(S32 r); static const S32 UNDEFINED=-1; }; diff --git a/indra/llui/llxyvector.cpp b/indra/llui/llxyvector.cpp index 40d5d8c903..19bd8465b9 100644 --- a/indra/llui/llxyvector.cpp +++ b/indra/llui/llxyvector.cpp @@ -25,7 +25,7 @@ * $/LicenseInfo$ */ -// A control that allows to set two related vector magnitudes by manipulating a single vector on a plane. +// A control that allows to set two related vector magnitudes by manipulating a single vector on a plane. #include "linden_common.h" @@ -158,7 +158,7 @@ void drawArrow(S32 tailX, S32 tailY, S32 tipX, S32 tipY, LLColor4 color) S32 dy = tipY - tailY; S32 arrowLength = (abs(dx) < ARROW_LENGTH_LONG && abs(dy) < ARROW_LENGTH_LONG) ? ARROW_LENGTH_SHORT : ARROW_LENGTH_LONG; - + F32 theta = std::atan2(dy, dx); F32 rad = ARROW_ANGLE * std::atan(1) * 4 / 180; -- cgit v1.2.3 From 8861264d8d3f4c9e8fa5f62ec57f73b0b2fd2ff0 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Wed, 12 Jun 2024 15:33:45 -0700 Subject: secondlife/viewer#1475: Fix Terrain tab controls no longer disabled when insufficient permissions --- indra/llui/llview.cpp | 10 +++++++++- indra/llui/llview.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 28283964e2..441b7d6a6c 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -591,12 +591,20 @@ void LLView::deleteAllChildren() updateBoundingRect(); } -void LLView::setAllChildrenEnabled(bool b) +void LLView::setAllChildrenEnabled(bool b, bool recursive /*= false*/) { for (LLView* viewp : mChildList) { viewp->setEnabled(b); } + + if (recursive) + { + for (LLView* viewp : mChildList) + { + viewp->setAllChildrenEnabled(b, recursive); + } + } } // virtual diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 3af748dda6..3ce7243370 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -287,7 +287,7 @@ public: // children, etc. virtual void deleteAllChildren(); - void setAllChildrenEnabled(bool b); + void setAllChildrenEnabled(bool b, bool recursive = false); virtual void setVisible(bool visible); void setVisibleDirect(bool visible) { mVisible = visible; } -- cgit v1.2.3 From fe76026a3fea79522083864019026e6bf01777df Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Mon, 22 Jul 2024 10:58:33 -0400 Subject: Fix nullptr crash in LLScrollListCtrl::getSelectedItemLabel --- indra/llui/llscrolllistctrl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 74a9641836..1d9564d107 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1479,7 +1479,11 @@ const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const item = getFirstSelected(); if (item) { - return item->getColumn(column)->getValue().asString(); + auto col = item->getColumn(column); + if(col) + { + return col->getValue().asString(); + } } return LLStringUtil::null; -- cgit v1.2.3 From 7cc3ff55b97b94a3b52daebfe072eb22192c710b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 16 Aug 2024 19:27:48 +0300 Subject: viewer#2296 Don't show 'are you sure you want to leave the call' when shutting down --- indra/llui/llfloater.cpp | 8 ++++++++ indra/llui/llfloater.h | 1 + 2 files changed, 9 insertions(+) (limited to 'indra/llui') diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index e6ecf3c283..f29f9286c9 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1926,6 +1926,14 @@ void LLFloater::onClickClose( LLFloater* self ) self->onClickCloseBtn(); } +// static +void LLFloater::onClickClose(LLFloater* self, bool app_quitting) +{ + if (!self) + return; + self->onClickCloseBtn(app_quitting); +} + void LLFloater::onClickCloseBtn(bool app_quitting) { closeFloater(false); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 3d75c42f60..9be2240f6f 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -363,6 +363,7 @@ public: // } static void onClickClose(LLFloater* floater); + static void onClickClose(LLFloater* floater, bool app_quitting); static void onClickMinimize(LLFloater* floater); static void onClickTearOff(LLFloater* floater); static void onClickDock(LLFloater* floater); -- cgit v1.2.3 From 03d7f2b84daf9ab991de6cad7d6149abda1ef716 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 28 Aug 2024 21:16:56 -0400 Subject: Ditch trailing spaces. --- indra/llui/llfloaterreglistener.cpp | 10 ++--- indra/llui/llfloaterreglistener.h | 10 ++--- indra/llui/llluafloater.cpp | 80 ++++++++++++++++++------------------- indra/llui/llluafloater.h | 10 ++--- indra/llui/llmenugl.h | 2 +- indra/llui/lluictrl.cpp | 2 +- 6 files changed, 57 insertions(+), 57 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp index e17f9f4dd6..b0dceb55c8 100644 --- a/indra/llui/llfloaterreglistener.cpp +++ b/indra/llui/llfloaterreglistener.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-08-12 * @brief Implementation for llfloaterreglistener. - * + * * $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$ */ diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h index 2165b1b62f..42e7178cbc 100644 --- a/indra/llui/llfloaterreglistener.h +++ b/indra/llui/llfloaterreglistener.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-08-12 * @brief Wrap (subset of) LLFloaterReg API with an event API - * + * * $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$ */ diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index b08508bf9b..ca80a2c451 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llluafloater.cpp * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, 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$ * @@ -68,25 +68,25 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : }; LLSD requiredParams = llsd::map("ctrl_name", LLSD(), "value", LLSD()); - mDispatchListener.add("set_enabled", "", [ctrl_lookup](const LLSD &event) - { + mDispatchListener.add("set_enabled", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setEnabled(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatchListener.add("set_visible", "", [ctrl_lookup](const LLSD &event) - { + mDispatchListener.add("set_visible", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setVisible(event["value"].asBoolean()); return LLSD(); }); }, requiredParams); - mDispatchListener.add("set_value", "", [ctrl_lookup](const LLSD &event) - { + mDispatchListener.add("set_value", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setValue(event["value"]); return LLSD(); }); }, requiredParams); - mDispatchListener.add("add_list_element", "", [this](const LLSD &event) - { + mDispatchListener.add("add_list_element", "", [this](const LLSD &event) + { LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) + if(ctrl) { - LLSD element_data = event["value"]; + LLSD element_data = event["value"]; if (element_data.isArray()) { for (const auto &row : llsd::inArray(element_data)) @@ -94,34 +94,34 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : ctrl->addElement(row); } } - else + else { ctrl->addElement(element_data); } } }, requiredParams); - mDispatchListener.add("clear_list", "", [this](const LLSD &event) - { + mDispatchListener.add("clear_list", "", [this](const LLSD &event) + { LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); - if(ctrl) + if(ctrl) { ctrl->deleteAllItems(); } }, llsd::map("ctrl_name", LLSD())); - mDispatchListener.add("add_text", "", [this](const LLSD &event) - { + mDispatchListener.add("add_text", "", [this](const LLSD &event) + { LLTextEditor *editor = getChild(event["ctrl_name"].asString()); - if (editor) + if (editor) { editor->pasteTextWithLinebreaks(stringize(event["value"])); editor->addLineBreakChar(true); } }, requiredParams); - mDispatchListener.add("set_label", "", [this](const LLSD &event) - { + mDispatchListener.add("set_label", "", [this](const LLSD &event) + { LLButton *btn = getChild(event["ctrl_name"].asString()); if (btn) { @@ -129,17 +129,17 @@ LLLuaFloater::LLLuaFloater(const LLSD &key) : } }, requiredParams); - mDispatchListener.add("set_title", "", [this](const LLSD &event) - { + mDispatchListener.add("set_title", "", [this](const LLSD &event) + { setTitle(event["value"].asString()); }, llsd::map("value", LLSD())); - - mDispatchListener.add("get_value", "", [ctrl_lookup](const LLSD &event) - { + + mDispatchListener.add("get_value", "", [ctrl_lookup](const LLSD &event) + { return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { return llsd::map("value", ctrl->getValue()); }); }, llsd::map("ctrl_name", LLSD(), "reqid", LLSD())); - mDispatchListener.add("get_selected_id", "", [this](const LLSD &event) + mDispatchListener.add("get_selected_id", "", [this](const LLSD &event) { LLScrollListCtrl *ctrl = getChild(event["ctrl_name"].asString()); if (!ctrl) @@ -157,7 +157,7 @@ LLLuaFloater::~LLLuaFloater() post(LLSD()); } -BOOL LLLuaFloater::postBuild() +BOOL LLLuaFloater::postBuild() { for (LLView *view : *getChildList()) { @@ -217,10 +217,10 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str auto mouse_event_cb = [this, data](LLUICtrl *ctrl, const LLSD ¶m) { post(data); }; - auto mouse_event_coords_cb = [this, data](LLUICtrl *ctrl, S32 x, S32 y, MASK mask) - { + auto mouse_event_coords_cb = [this, data](LLUICtrl *ctrl, S32 x, S32 y, MASK mask) + { LLSD event(data); - post(event.with("x", x).with("y", y)); + post(event.with("x", x).with("y", y)); }; auto post_with_value = [this, data](LLSD value) @@ -260,12 +260,12 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str { list->setDoubleClickCallback( [post_with_value, list](){ post_with_value(LLSD(list->getCurrentID())); }); } - else + else { ctrl->setDoubleClickCallback(mouse_event_coords_cb); } } - else if (event_is(event, "keystroke")) + else if (event_is(event, "keystroke")) { LLTextEditor* text_editor = dynamic_cast(ctrl); if (text_editor) @@ -278,7 +278,7 @@ void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::str line_editor->setKeystrokeCallback([post_with_value](LLLineEditor *editor, void* userdata) { post_with_value(editor->getValue()); }, NULL); } } - else + else { LL_WARNS("LuaFloater") << "Can't register callback for unknown event: " << event << " , control: " << ctrl_name << LL_ENDL; } @@ -292,7 +292,7 @@ void LLLuaFloater::post(const LLSD &data) LLEventPumps::instance().obtain(mReplyPumpName).post(stamped_data); } -void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) +void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) { llassert(EVENT_LIST.find(event_name) != EVENT_LIST.end()); post(data.with("event", event_name)); @@ -302,12 +302,12 @@ void LLLuaFloater::showLuaFloater(const LLSD &data) { fsyspath fs_path(data["xml_path"].asString()); std::string path = fs_path.lexically_normal().u8string(); - if (!fs_path.is_absolute()) + if (!fs_path.is_absolute()) { std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); path = (fsyspath(lib_path) / path).u8string(); } - + LLLuaFloater *floater = new LLLuaFloater(data); floater->buildFromFile(path); floater->openFloater(floater->getKey()); diff --git a/indra/llui/llluafloater.h b/indra/llui/llluafloater.h index ccc3ccb39b..d0641d41b0 100644 --- a/indra/llui/llluafloater.h +++ b/indra/llui/llluafloater.h @@ -1,24 +1,24 @@ -/** +/** * @file llluafloater.h * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, 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$ */ diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index b6b83c4716..65211bf122 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -953,7 +953,7 @@ public: typedef LLUICtrl::CommitCallbackInfo cb_info; static void addCommit(view_listener_t *listener, const std::string &name, cb_info::EUntrustedCall handle_untrusted = cb_info::UNTRUSTED_ALLOW) { - LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, cb_info([listener](LLUICtrl*, const LLSD& param){ return listener->handleEvent(param); }, handle_untrusted)); } diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index d6631a9cd9..f1dc3d7c08 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -284,7 +284,7 @@ LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCa std::string function_name = cb.function_name; setFunctionName(function_name); LLUICtrl::CommitCallbackInfo *info = (CommitCallbackRegistry::getValue(function_name)); - if (info && info->callback_func) + if (info && info->callback_func) { if (cb.parameter.isProvided()) return boost::bind((info->callback_func), _1, cb.parameter); -- cgit v1.2.3 From c4f29a535359fe1ecdb8bec45596f40b02891cd1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 4 Sep 2024 14:17:32 -0400 Subject: Resolve a few unresolved merge conflicts. --- indra/llui/llmenugl.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 65211bf122..da5d7874c6 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -562,6 +562,8 @@ public: // add a context menu branch bool appendContextSubMenu(LLMenuGL *menu); + void createSpilloverBranch(); + void cleanupSpilloverBranch(); // Add the menu item to this menu. virtual bool append( LLMenuItemGL* item ); @@ -806,15 +808,9 @@ public: void resetMenuTrigger() { mAltKeyTrigger = false; } // add a menu - this will create a drop down menu. -<<<<<<< variant A - virtual BOOL appendMenu(LLMenuGL *menu); + virtual bool appendMenu(LLMenuGL *menu); private: ->>>>>>> variant B - virtual bool appendMenu( LLMenuGL* menu ); -####### Ancestor - virtual BOOL appendMenu( LLMenuGL* menu ); -======= end // rearrange the child rects so they fit the shape of the menu // bar. virtual void arrange( void ); -- cgit v1.2.3 From a980a8f30870d35a5a45cbf52169f7bf35cf95ce Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 4 Sep 2024 14:23:31 -0400 Subject: Swat a few more buzzing BOOLs. --- indra/llui/llluafloater.cpp | 2 +- indra/llui/llluafloater.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index ca80a2c451..ccdadc6ae0 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -157,7 +157,7 @@ LLLuaFloater::~LLLuaFloater() post(LLSD()); } -BOOL LLLuaFloater::postBuild() +bool LLLuaFloater::postBuild() { for (LLView *view : *getChildList()) { diff --git a/indra/llui/llluafloater.h b/indra/llui/llluafloater.h index d0641d41b0..41132f926d 100644 --- a/indra/llui/llluafloater.h +++ b/indra/llui/llluafloater.h @@ -34,7 +34,7 @@ class LLLuaFloater : public LLFloater { public: LLLuaFloater(const LLSD &key); - BOOL postBuild(); + bool postBuild(); virtual ~LLLuaFloater(); void registerCallback(const std::string &ctrl_name, const std::string &event); -- cgit v1.2.3